Mimicking IID_PPV_ARGS

Steven Schveighoffer schveiguy at gmail.com
Wed Aug 29 16:17:13 UTC 2018


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


More information about the Digitalmars-d-learn mailing list