Mimicking IID_PPV_ARGS
Cody Duncan
dlang at codyduncan.net
Thu Aug 30 05:43:51 UTC 2018
On Wednesday, 29 August 2018 at 16:17:13 UTC, Steven
Schveighoffer wrote:
> On 8/29/18 5:51 AM, Cody Duncan wrote:
>> From C++
>> #define IID_PPV_ARGS(ppType) __uuidof(**(ppType)),
>> IID_PPV_ARGS_Helper(ppType)
>>
>> //ComPtr version
>> ComPtr<ID3D12Debug> debugController;
>> if
>> (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController))))
>> {
>> // do stuff
>> }
>>
>> //Raw Pointer Version
>> ID3D12Debug* debugController;
>> if
>> (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController))))
>> {
>> // do stuff
>> }
>>
>> IID_PPV_ARGS expands into two arguments of types (const IID &,
>> void**).
>>
>> The first arg expansion uses a compiler intrinsic to look up
>> the GUID of the type.
>>
>> The second arg expansion uses an IID_PPV_ARGS_Helper, a
>> templated function that return a void**. This either forwards
>> the address of the pointer passed in,
>> or the result of ReleaseAndGetAddressOf() from the com object
>> within the ComPtr.
>>
>>
>>
>> Now in D!
>> Here is my attempt so far, pared down to just enough to
>> compile "IID_PPV_ARGS(debugController).expand".
>>
>> // ----- uuidtemplate.d -----
>> module uuidtemplate;
>> import core.sys.windows.com;
>>
>> mixin( uuid!(IUnknown,
>> "00000000-0000-0000-C000-000000000046"));
>> mixin(
>> uuid!(IClassFactory,"00000001-0000-0000-C000-000000000046"));
>>
>> template uuid(T, const char[] g) {
>> const char [] uuid =
>> "const IID IID_"~T.stringof~"={ 0x" ~ g[0..8] ~ ",0x"
>> ~ g[9..13] ~ ",0x" ~ g[14..18] ~ ",[0x" ~ g[19..21] ~ ",0x" ~
>> g[21..23] ~ ",0x" ~ g[24..26] ~ ",0x" ~ g[26..28] ~ ",0x" ~
>> g[28..30] ~ ",0x" ~ g[30..32] ~ ",0x" ~ g[32..34] ~ ",0x" ~
>> g[34..36] ~ "]};"~
>> "template uuidof(T:"~T.stringof~"){"~
>> " const IID uuidof ={ 0x" ~ g[0..8] ~ ",0x" ~
>> g[9..13] ~ ",0x" ~ g[14..18] ~ ",[0x" ~ g[19..21] ~ ",0x" ~
>> g[21..23] ~ ",0x" ~ g[24..26] ~ ",0x" ~ g[26..28] ~ ",0x" ~
>> g[28..30] ~ ",0x" ~ g[30..32] ~ ",0x" ~ g[32..34] ~ ",0x" ~
>> g[34..36] ~ "]};"~
>> "}";
>> }
>>
>> // ----- debuginterface.d -----
>> module debuginterface;
>>
>> import core.sys.windows.com;
>> import uuidtemplate : uuid; // uuidof
>>
>> mixin(uuid!(ID3D12Debug,
>> "344488b7-6846-474b-b989-f027448245e0"));
>> interface ID3D12Debug : IUnknown {
>> void EnableDebugLayer();
>> }
>>
>> // ----- comptr.d -----
>> module comptr;
>> public import std.typecons;
>> import core.sys.windows.com;
>> import uuidtemplate : uuid; // uuidof
>>
>> mixin template IID_PPV_ARGS()
>> {
>> auto IID_PPV_ARGS(T : ComPtr!U, U)(ref T arg)
>> {
>> return tuple(&uuidof!(U), arg.ptrRef());
>> }
>>
>> auto IID_PPV_ARGS(T)(ref T arg)
>> {
>> return tuple(&uuidof!(T), &arg);
>> }
>> }
>>
>> template ComPtr(T)
>> {
>> struct ComPtr
>> {
>> protected:
>> T _ptr = null;
>>
>> public:
>> this(ref T inVar)
>> {
>> _ptr = inVar;
>> }
>>
>> @property T ptr() { return _ptr; }
>> @property T* ptrRef() { return &_ptr; }
>>
>> alias ptr this;
>> }
>> }
>>
>>
>> // ----- app.d -----
>> module app;
>>
>> import core.sys.windows.com;
>> import comptr;
>> import debuginterface;
>> import std.stdio;
>>
>> // Necessary to mixin since the declaration in comptr.d
>> // cannot see the uuidof declared by debuginterface.
>> // When removing the mixin, it always returns the GUID for
>> IUnknown.
>> mixin IID_PPV_ARGS;
>>
>> bool D3D12GetDebugInterface(const (GUID)* riid, ID3D12Debug*
>> ppvDebug)
>> {
>> printf("Guid =
>> {%08lX-%04hX-%04hX-%02hhX%02hhX-%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX}",
>> riid.Data1, riid.Data2, riid.Data3,
>> riid.Data4[0], riid.Data4[1], riid.Data4[2],
>> riid.Data4[3],
>> riid.Data4[4], riid.Data4[5], riid.Data4[6],
>> riid.Data4[7]);
>>
>> // output should be:
>> "344488b7-6846-474b-b989-f027448245e0"
>>
>> return true;
>> }
>>
>> void main()
>> {
>> ComPtr!ID3D12Debug debugController;
>>
>> // I want to get rid of the .expand syntax here
>> if
>> (D3D12GetDebugInterface(IID_PPV_ARGS(debugController).expand))
>> {
>> writefln("success!");
>> }
>> }
>>
>> /// end code
>>
>> So my goal here is to get the syntax for IID_PPV_ARGS down to
>> looking just like a function call.
>>
>> I'd like to get rid of the .expand call. I haven't figured out
>> a better way to express converting the ComPtr input into two
>> outputs used as separate arguments into the outer function.
>>
>> Any ideas?
>
> Unfortunately, D does not allow you to return an alias sequence
> directly. So you have to return a struct, which is going to be
> utilized instead of the alias this'd alias sequence inside the
> tuple. This is why it's necessary for the expand call.
>
> However, it's not much different to accept the struct than it
> is to accept the 2 items in the struct. Why not just modify the
> function D3D12... to accept a struct instead of the 2
> parameters? You could even overload it, if you still want to
> call it directly:
>
> bool D3D12GetDebugInterface(Tuple!(const (GUID)*, ID3D12Debug*)
> param)
> {
> return D3D12GetDebugInterface(param.expand);
> }
>
> My preference would be to encompass all the logic for pairing
> the 2 items together into a specialized struct, not a tuple.
>
> -Steve
I like the idea of an adapter function, but I'm not a huge fan of
the prospect to generate adapters for the hundreds of DirectX API
calls. I may rethink that as I get deeper into my hobby project.
But now I know there's not a really a way to express the syntax I
want.
For my other attempt I did try a mixin:
template IID_PPV_ARGS(alias arg) {
static if(__traits(isSame, TemplateOf!(typeof(arg)), ComPtr))
{
// The arg is a ComPtr, reference the InterfaceType for a
typename.
const char [] IID_PPV_ARGS =
"from!\"std.typecons\".tuple(&IID_"~arg.InterfaceType.stringof~
", "~__traits(identifier, arg)~".ptrRef()).expand";
}
else
{
// The arg is a pointer, get the typename directly.
const char [] IID_PPV_ARGS =
"from!\"std.typecons\".tuple(&IID_"~typeof(arg).stringof~
", &"~__traits(identifier, arg)~").expand";
}
}
ComPtr!ID3D12Debug debugController;
D3D12GetDebugInterface(mixin(IID_PPV_ARGS_mix!(debugController)))
It's still using a tuple.expand, but it writes it in place.
However, I get an error that I don't understand with the variable
used is from an array:
// list of command allocators as a class member
ComPtr!ID3D12CommandAllocator[] m_commandAllocators;
// inside a class member function
m_device.CreateCommandAllocator(
D3D12_COMMAND_LIST_TYPE_DIRECT,
mixin(IID_PPV_ARGS_mix!(m_commandAllocators[0])));
renderer.d(348,39): Error: value of this is not known at compile
time
(this is in bold font)
I'm not sure what this error means. I don't know what it's
referring to by "this". As far as I understand, the type of the
argument into the alias is a ComPtr!ID3D12CommandAllocator.
Another example with a variable of that type from a lone member
variable works just fine:
// single command allocators as a class member
ComPtr!ID3D12CommandAllocator m_oneCommandAllocator;
// inside a class member function
m_device.CreateCommandAllocator(
D3D12_COMMAND_LIST_TYPE_DIRECT,
mixin(IID_PPV_ARGS_mix!(m_oneCommandAllocator)));
More information about the Digitalmars-d-learn
mailing list