Thread overview
Quirks of 'alias foo this'
Apr 26, 2011
Sean Cavanaugh
Apr 26, 2011
Andrej Mitrovic
Apr 26, 2011
Andrej Mitrovic
April 26, 2011
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;
        }
    }
}

April 26, 2011
I have a similar problem. :)

I'd like to catch a call to a COM object to check if it was properly initialized first.

Here's one way to do it with regular classes: http://codepad.org/lHsQf4UG

You can read about whitehole here: http://www.digitalmars.com/d/2.0/phobos/std_typecons.html#WhiteHole

I think the above example could be used with COM objects but I haven't tried yet.
April 26, 2011
Well I've tried it with a COM object and it seems to work. So I'll definitely use this technique now. I do wish whitehole gave me the option to throw a custom error message.