twinbasic / lang-design

Language Design for twinBASIC
MIT License
11 stars 1 forks source link

Allow 'As Any' as function return type in interfaces. #60

Open fafalone opened 1 year ago

fafalone commented 1 year ago

Recently encountered this in an SDK header:

            [in] PATTERNID patternId,
            [in] REFIID riid,
            [out, iid_is(riid), retval] void** patternObject);

As Any would be supported if it wasn't the retval; there's no reason to not allow it as the return.

wqweto commented 1 year ago

In this case As IUnknown would be enough. I mean the retval is a pointer to a pointer, not a pure void * which would carry As Any semantics in TB/VBx.

fafalone commented 1 year ago

Funny enough, the method, GetCurrentPatternAs, has this comment:

// These versions exist to simplify using these methods, avoiding the extra Query // Interface step that inevitably follows getting the Pattern.

and is followed by

        HRESULT GetCurrentPattern(
            [in] PATTERNID patternId,
            [out, retval] IUnknown** patternObject);
wqweto commented 1 year ago

Yes, this is a common pattern the pair of params -- [in] (ref to) IID + [out] (pointer to) interface pointer -- so that one can get an output pointer and directly cast it to a specific interface which happens to be the type of the local (receiving) variable.

For instance look at OleCreatePictureIndirect API, it has the same pair and everyone is passing IID_IPicture instead of just using IID_IUnknown and declaring API's last param As IUnknown but pass an StdPicture variable.

The VBx compiler at callsite automagically will emit QI call on returned pointer for whichever interface the actual argument is i.e. QI for IPictureDisp in case of StdPicture variable.

I'm not sure if this extra QI call is emitted when receiving variable and declared parameter interface type match. Wayne can chime in how TB deals with this and if the optimization is worth it.

fafalone commented 1 year ago

Very common. Since MKTYPLIB doesn't support the iid_is attribute, I've deleted hundreds of them from interfaces while working on oleexp; and of course they're a staple of the shell interfaces I use every day. Out as IUnknown instead of void** is less common, but I've still seen it a dozen or two times.

But using IID_IUnknown and declaring every prototype as IUnknown makes code less readable as a minimum... it's not a good habit to get into.

wqweto commented 1 year ago

Well, using void ** on the prototype has the same meaning as using As IUnknown in VBx i.e. type is unknown and can differ from call to call with the caveat that the explicit riid param is responsible for type info at run-time.

fafalone commented 1 year ago

This was an example, not a request for a custom handler for exactly this call and ones exactly like it.