Pointers to non-static member functions!

Michel Fortin michel.fortin at michelf.com
Wed Jun 8 11:13:46 PDT 2011


On 2011-06-08 13:29:27 -0400, "Steven Schveighoffer" 
<schveiguy at yahoo.com> said:

> On Wed, 08 Jun 2011 13:07:50 -0400, Michel Fortin  
> <michel.fortin at michelf.com> wrote:
> 
>> On 2011-06-08 10:54:54 -0400, "Steven Schveighoffer"  
>> <schveiguy at yahoo.com> said:
>> 
>>> On Wed, 08 Jun 2011 10:40:48 -0400, Daniel Murphy   
>>> <yebblies at nospamgmail.com> wrote:
>>> 
>>>  I almost would prefer that hacking delegates would be illegal.  Yes,  
>>> you  can get the function pointer and data pointer out of a delegate,  
>>> but  cannot set them.  I can't think of any good use cases for them.
>> 
>> I was "hacking delegates" a lot in the D/Objective-C bridge (before I  
>> decided to bring things directly in the compiler). To make it short, I  
>> needed to do that to call the appropriate D function from Objective-C  
>> stub method implementation.
> 
> The issue with hacking delegates I have is that almost certainly you 
> know  what the type of the function pointer is, which means you almost 
> certainly  have access to the member function for that type directly, 
> it seems a very  strange use case to be able to dynamically address the 
> member functions of  a class without knowing the type.  When I first 
> wrote the above reason, I  tried to think of a compelling real example, 
> I couldn't.
> 
> Now, you are implementing a bridge to another language that supports a  
> feature, which means you have to implement it.  But how often is that  
> feature used in that other language?

This is not at all a feature of Objective-C. This was a workaround. 
Tell me, if inside a template you have an alias to a member function, 
how can I call that member function? This won't work:

	void callMe(alias memberFunc)(Object o) {
		o.memberFunc(); // won't work, Object has no member called "memberFunc".
	}

What you can do is this:

	template Binding(alias memberFunc)(Object o) {
		void delegate() delegateToMember;
		delegateToMember.ptr = o;
		delegateToMember.funcptr = &memberFunc;
		delegateToMember();
	}

Although even there it won't work very well if your member is a virtual 
function. I needed to use an ever stranger workaround for that. Here's 
the actual code from the Objective-C bridge (it's a template which be 
mixed in the class):

	/**
	 * Template which can be mixed in a class to bind selector sel to instance
	 * method.
	 *
	 * This will create a receiver function which will forward the call to
	 * method, decapsulating arguments and encapsulating the return value as
	 * appropriate.
	 */
	template ObjcBindMethod(alias method, R, char[] sel, A...) {
	
		/**
		 * Resolve virtual function and return a delegate to it. This must be
		 * done from a non-static member function otherwise we can't get the
		 * address of the overriding function if there is one.
		 */
		private R delegate(A) resolveVirtualCall() {
			return &method;
		}
	
		/** Call forwarder for a virtual method of this class. */
		private static
		R forwardVirtualCall(objc.runtime.id self, objc.runtime.SEL _cmd, A args) {
			R delegate(A) delegate() resolve;
			resolve.ptr = objc.msg.send!(void*)(self, 
objc.method.selector!("d_object"));
			resolve.funcptr = &resolveVirtualCall;
			return resolve()(args);
		}
	
		mixin objc.bridge.ObjcMethodInfo!(forwardVirtualCall, R, sel, A);
	}

Here, the entry point from Objective-C is the forwardVirtualCall 
function, which does two things: it finds the object pointer, and then 
it resolves the virtual call, which can only be done in a non-static 
member function (resolveVirtualCall) because in the absence of an 
implicit this pointer it won't work.

Actually that's not really the entry point from Objective-C, there's a 
first entry point that performs argument conversion as necessary which 
then calls this one to call the right virtual function, but that's 
irrelevant to our case.

What would have really simplified things in all this is a C++-style 
pointer to member function type. But that's not part of D.


>>> BTW, if the calling convention is different, we may be able to allow it 
>>>   via creating a "this" function pointer:
>>>  assert(is(typeof(&A.func2) == int function(A this)));
>>>  Since this is a keyword, it can be interpreted as a different calling  
>>>  convention.
>> 
>> I was thinking about that too. Note that it forces int function(A this) 
>>  to be implicitly castable to int function(void* this) when assigning 
>> to  a delegate.
> 
> ATM, delegates and functions do not implicitly cast whatsoever.  In 
> fact,  I would not want the above cast to succeed, what should be 
> implicitly cast  is a contravariant function.  For example int 
> function(Object this) should  implicitly cast to int function(A this).

You're right.


> Now, a delegate does not record its 'this' type, so getting the 
> function  pointer for the delegate should yield an uncallable function 
> pointer.  I  proposed in another part of this thread the syntax:
> 
> int function(void this);
> 
> This at least gives you type information, but doesn't allow you to  
> unsafely call it.  If you wish to explicitly cast it to a viable 
> function,  you can.

Makes sense.


> All this is dependent on the idea that a delegate cannot be constructed 
>  unless you have the original method (or a type-safe 
> 'delegate-function'  such as int function(A this)).  I really think 
> that makes sense.

I won't disagree with anything you say, because I do agree. I know my 
use case is a fringe one, and I don't need it anymore so I don't care 
much. And it'd only need one small thing for it to work without all 
that cumbersome hack:

	o.memberFunc() // where methodFunc is an alias to a member of o

which probably shouldn't be that difficult to implement in the 
compiler, what's needed is new syntax to distinguish it from a normal 
member call. Perhaps this?

	o.(memberFunc)()


-- 
Michel Fortin
michel.fortin at michelf.com
http://michelf.com/



More information about the Digitalmars-d mailing list