Mimicking IID_PPV_ARGS

Steven Schveighoffer schveiguy at gmail.com
Thu Aug 30 14:09:43 UTC 2018


On 8/30/18 1:43 AM, Cody Duncan wrote:
> 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.
>>
> 
> 
> 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.

Adapters may be possible to generate automatically with a mixin and a 
function name.

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

I think because you can't alias an indexed item from the struct. Aliases 
must strictly be symbols, not arbitrary expressions. Essentially, it's 
trying to evaluate m_commandAllocators[0], then take an alias to that. 
It can't evaluate the expression at compile time, so it gives up.

Possibly, you could create an inner function that returns it, and alias 
that function, but I'm not sure whether it would work.

e.g.:

auto getIt() { return m_commandAllocators[0]; }

...mixin(IID_PPV_ARGS_mix!(getIt)))

-Steve


More information about the Digitalmars-d-learn mailing list