July 31, 2008
Steven Schveighoffer wrote:
> "Kirk McDonald" wrote
> 
>>Steven Schveighoffer wrote:
>>
>>>"ws" wrote
>>>
>>>
>>>>Suppose i have the following:
>>>>
>>>>int function() fp;
>>>>int *ptr;
>>>>class Cls
>>>>{
>>>>int k;
>>>>int foo()  {  return 0;  }
>>>>}
>>>>
>>>>void main()
>>>>{
>>>>fp = &Cls.foo;
>>>>
>>>>Cls c = new Cls;
>>>>ptr = &c.k;  // <-- why always need a new instance?
>>>>}
>>>>
>>>>As compared to delegate, is there no analogous way to specify this?
>>>>
>>>>ptr = &Cls.k
>>>>
>>>>Thanks!
>>>
>>>
>>>In fact, this should fail to compile.  The fact that it succeeds is a bug. You should enter it in bugzilla (is it already there?)
>>>
>>>Did you try running it?  I get a segfault.
>>>
>>>fp is a member function, which means it needs a hidden 'this' pointer. When you assign &Cls.foo to fp, this should result in the same error as if you typed Cls.foo():
>>>
>>>Error: need 'this' to access member foo
>>>
>>>-Steve
>>
>>A direct, C++-style pointer-to-member-function is occasionally useful. (Though rarely, since delegates are more useful nearly every time.) Allowing this syntax is the only sensible way to get access to them. The resulting pointer can then manually be shoved into a delegate:
>>
>>void function() fp = &Cls.foo;
>>Cls c = new Cls;
>>void delegate() dg;
>>dg.funcptr = fp;
>>dg.ptr = c; // I forget if a cast to void* is necessary...
>>dg();
>>
>>Pyd relies on this behavior to implement its class wrapping, and it would be fairly inconvenient if it went away.
> 
> 
> I had no idea you could build delegates this way.  But even still, the function signature is not correct, it should be something like:
> 
> void function(Cls c) fp = &Cls.foo;
> 
> But in any case, the original code seems like dangerous behavior to allow without casting.  There should at least be a cast involved so the user is forced to say "yes, I know that the function signature isn't correct, do it anyways".  I still say this should be a bug.
> 
> -Steve 
> 
> 

The only really correct (that is, type-safe) solution is to do something like what C++ does. The syntax for this is astoundingly ugly, but let me walk you through it. (This is C++ code, so brace yourself.) :-)

class C {
public:
    virtual void foo()=0; // an abstract ("pure virtual") method
};

// Alias the pointer type.
typedef void (C::* ptr_t)();

void f(C* c) {
    // Take the address of the method.
    ptr_t fn = &C::foo;
    // Call it on the instance.
    (c->*fn)();
}

Several things deserve mention:

1) The syntax is just weird. Take this:

typedef void (C::* ptr_t)();

That is aliasing the type "void (C::*)()" to the name ptr_t. That type is a function pointer type. It's like a void function() which can only apply to member functions of class C. C::* basically means "pointer to member of C."

On the other side of the equation, we have:

(c->*fn)();

This is binding the pointer-to-member "fn" to the instance pointed to by c, and then calling the result. In the example, it is basically equivalent to:

c->foo();

->* is an operator in its own right in C++.

2) It does a virtual call!

Where a pointer-to-member-function is a regular function pointer in D, in C++ it is basically just the index of the function in the vtable. (Unless, of course, the member function isn't virtual.)

As an aside, it is precisely the non-virtual nature of these pointers-to-member-functions that Pyd exploits. The details behind this chicanery are more involved than I want to describe here.

3) The pointers are type-safe. This bears mentioning again: Only member functions of class C (which have that particular signature) can be assigned to a variable of that ptr_t type above. This is exactly in line with what you were saying: void function() is not the correct type for the function pointer.

As it is, pointers-to-member-functions are basically a half-feature in D. You can do it, but it arguably breaks the type system in weird ways. I would argue that this is fine, since they are innately ugly.

-- 
Kirk McDonald
http://kirkmcdonald.blogspot.com
Pyd: Connecting D and Python
http://pyd.dsource.org
1 2
Next ›   Last »