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