Member function pointers
Adam D. Ruppe
destructionator at gmail.com
Fri Jun 7 17:24:22 PDT 2013
On Friday, 7 June 2013 at 23:54:55 UTC, Manu wrote:
> The properties are already there...
> but they're not properly typed.
I just don't think they can be unless we change the visible type
which isn't always what we want.... but, check this out:
// this new type keeps track of the exact type of the pointer
// and manages the delegate so we can cast with some sanity...
struct PointerToMemberFunction(Class, Ret, T...) if(is(Class :
Object)) {
private Ret delegate(T) dg;
@property Class object() { return cast(Class) dg.ptr; }
@property void object(Class rhs) { dg.ptr = cast(void*) rhs; }
Ret opCall(T t) {
assert(dg.ptr !is null, "null this");
static if(is(Ret == void))
dg(t);
else
return dg(t);
}
}
// this helps us construct the above
template ptrToMember(alias blargh) {
// I'm writing out the function template longhand
// because I want to use blargh as a type and
// dmd won't let me do it without an intermediate
// dmd complains "type expected, not __traits" so we use
// this to work around it
template workaround(T) { alias workaround = T; }
alias ObjectType = workaround!(__traits(parent, blargh));
auto ptrToMember(ObjectType a = null) {
import std.traits;
PointerToMemberFunction!(
ObjectType,
ReturnType!blargh,
ParameterTypeTuple!blargh
) mem;
mem.dg.funcptr = &blargh;
mem.dg.ptr = cast(void*) a;
return mem;
}
}
actually i just realized maybe this should not be a function at
all, so it is easy to use as a class member, without having to
write an extra typeof(). That's probably more valuable than the
initialiser which as you'll see below is optional anyway.
Anyway, then you can use it like this:
class A {
void foo() {writeln("foo from ", name);}
int bar(string s) {writeln("bar ",s," from ", name); return 10; }
this(string n) { name = n; }
string name;
}
class B : A {
this(string n) { super(n); }
override void foo() { writeln("derived foo from ", name); }
}
void main() {
A a = new A("a");
B c = new B("c");
Object ob = a;
auto ptr = ptrToMember!(B.foo)(c); // initialize the object here
ptr();
ptr.object = a;
ptr();
auto ptr2 = ptrToMember!(A.bar); // initialize to null..
ptr2.object = ptr.object; // can assign later
ptr2("hey");
}
And if you play around with the types there, you'll see the
compile will fail if you do something uncool. Though the error
messages sometimes suck, what the hell: "test2.d(58): Error: not
a property ptr.object"... actually it is, but I was passing it an
A when it required a B. That's a type mismatch, not a missing
property.
But whatever, at least it *did* fail to compile, so we have our
type safety.
And if I threw in an alias this on a getter property, we could
assign these beasties to regular delegates too. Not half bad.
But regular delegate assigning to this is prohibited because we
can't be sure their context pointer is the right kind of class.
This would be true if the compiler automatically did the
ptrToMember!() too, so let's say we change &a.foo to return one
of these strongly typed things instead of a void* delegate like
it does now.
auto ptr = &a.foo; // is ptr a void delegate() or a pointer to
specifically a void() member of class A?
That matters because if the former (current behavior), this
compiles:
ptr = { writeln("hello!"); };
but if the latter, that will not, it will complain that the this
pointers are of mismatching type (or "not a property" lol). Of
course, with alias this style behavior, you could explicitly
write out
void delegate() ptr = &a.foo; // ok, use looser type
ptr = {...}; // perfectly fine
So I guess we wouldn't be losing much, but since we both agree a
delegate is almost always what we want, maybe the library
solution of ptrToMember is better to keep auto in a Just Works
state.
I'm presuming you're already doing something similar for your C++
interop, if not, I'm pretty sure this same idea would work there
too, at least to a 'good enough' state, even if not technically
portable.
> Yeah, that was my initial feeling too... and I think it could
> be quite workable in that way.
Aye, and this would require the compiler's help. Can't hit 'good
enough' on that with templates.
More information about the Digitalmars-d
mailing list