So my research into making a nice friendly to use COM interface wrapper
for D has had a few odd turns and I am wondering if there is an answer
to making the implementation nicer.
I discovered the 'alias foo this' syntax to let structs nearly
seamlessly impersonate a member variable. This has turned out to solve
most of my original need to wrap the functions, but it is imperfect.
The main problem with 'alias foo this' is that I am having is that I
can't find a way to catch code reading the aliased variable, in cases of
assignment or implicit conversion to foo type. I can catch writes just
fine with opAssign, but finding a way to overload the reads have me stumped.
I did some experiments with wraping the methods with some mixin
templates, but using 'alias foo this' is about 100% more useful,
intuitive and 99.9% less code to write :)
Examples (The fully ComPtr code is down further):
// initializes to null by default
ComPtr!(ID3D11Device) device;
ComPtr!(ID3D11Device) otherdevice;
// The 'device' argument to D3D11CreateDevice is implemented as
// 'out ID3D11Device', and uses the 'alias p this' feature to
// auto-magically write directly into device.p; Ideally
// I could hook this and either call SafeRelease here or assert
// that the p variable is null before being written to.
// This also represents the a case that you can write to the
// struct without detecting it.
HRESULT rslt = D3D11CreateDevice(
null,
D3D11_DRIVER_TYPE.HARDWARE,
null,
0 | D3D11_CREATE_DEVICE.DEBUG,
null,
0,
D3D11_SDK_VERSION,
device,
&featureLevel,
null);
// post-blit case, works
otherdevice = device;
// gives me a copy of 'p' due to 'alias p this'
ID3D11Device rawdevice = device;
// assignment back the other direction is caught by opAssign
// this is also the code path used if there are multiple COM
// interfaces in the hierarchy (IUnknown->ID3D11Resource->ID3D11Texture)
// and post-blit isn't used because the types are different.
device = rawdevice;
My current version of ComPtr:
struct ComPtr(T)
{
public:
static assert(is(T : std.c.windows.com.IUnknown) || is(T :
win32.unknwn.IUnknown));
T p;
alias p this;
private:
this(T inPtr)
{
p = inPtr;
}
public:
this(this)
{
if (p !is null)
{
p.AddRef();
}
}
~this()
{
SafeRelease();
}
// Attach and Detach set/unset the pointer without messing with the
refcount (unlike opAssign assignment)
void Attach(T other)
{
SafeRelease();
p = other;
}
T Detach()
{
T rval = p;
p = null;
return rval;
}
const bool HasData()
{
return (p !is null);
}
void opAssign(T other)
{
if (other !is null)
{
other.AddRef();
SafeRelease();
p = other;
}
else
{
SafeRelease();
}
}
void SafeRelease()
{
if (p !is null)
{
p.Release();
p = null;
}
}
}