<div dir="ltr">On 8 June 2013 10:24, Adam D. Ruppe <span dir="ltr"><<a href="mailto:destructionator@gmail.com" target="_blank">destructionator@gmail.com</a>></span> wrote:<br><div class="gmail_extra"><div class="gmail_quote">
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div class="im">On Friday, 7 June 2013 at 23:54:55 UTC, Manu wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
The properties are already there...<br>
but they're not properly typed.<br>
</blockquote>
<br></div>
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:<br>
<br>
// this new type keeps track of the exact type of the pointer<br>
// and manages the delegate so we can cast with some sanity...<br>
struct PointerToMemberFunction(Class, Ret, T...) if(is(Class : Object)) {<br>
        private Ret delegate(T) dg;<br>
        @property Class object() { return cast(Class) dg.ptr; }<br>
        @property void object(Class rhs) { dg.ptr = cast(void*) rhs; }<br>
        Ret opCall(T t) {<br>
                assert(dg.ptr !is null, "null this");<br>
                static if(is(Ret == void))<br>
                        dg(t);<br>
                else<br>
                        return dg(t);<br>
        }<br>
}<br>
<br>
// this helps us construct the above<br>
template ptrToMember(alias blargh) {<br>
        // I'm writing out the function template longhand<br>
        // because I want to use blargh as a type and<br>
        // dmd won't let me do it without an intermediate<br>
<br>
        // dmd complains "type expected, not __traits" so we use<br>
        // this to work around it<br>
        template workaround(T) { alias workaround = T; }<br>
<br>
        alias ObjectType = workaround!(__traits(parent, blargh));<br>
<br>
        auto ptrToMember(ObjectType a = null) {<br>
                import std.traits;<br>
                PointerToMemberFunction!(<br>
                        ObjectType,<br>
                        ReturnType!blargh,<br>
                        ParameterTypeTuple!blargh<br>
                ) mem;<br>
                mem.dg.funcptr = &blargh;<br>
                mem.dg.ptr = cast(void*) a;<br>
                return mem;<br>
        }<br>
}<br>
<br>
<br>
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.<br>

<br>
Anyway, then you can use it like this:<br>
<br>
class A {<br>
        void foo() {writeln("foo from ", name);}<br>
        int bar(string s) {writeln("bar ",s," from ", name); return 10; }<br>
        this(string n) { name = n; }<br>
        string name;<br>
}<br>
<br>
class B : A {<br>
        this(string n) { super(n); }<br>
        override void foo() { writeln("derived foo from ", name); }<br>
}<br>
<br>
<br>
void main() {<br>
        A a = new A("a");<br>
        B c = new B("c");<br>
        Object ob = a;<br>
<br>
        auto ptr = ptrToMember!(B.foo)(c); // initialize the object here<br>
        ptr();<br>
        ptr.object = a;<br>
        ptr();<br>
<br>
        auto ptr2 = ptrToMember!(A.bar); // initialize to null..<br>
        ptr2.object = ptr.object; // can assign later<br>
        ptr2("hey");<br>
}<br>
<br>
<br>
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.<br>

<br>
But whatever, at least it *did* fail to compile, so we have our type safety.<br>
<br>
<br>
And if I threw in an alias this on a getter property, we could assign these beasties to regular delegates too. Not half bad.<br>
<br>
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.<br>

<br>
auto ptr = &a.foo; // is ptr a void delegate() or a pointer to specifically a void() member of class A?<br>
<br>
<br>
That matters because if the former (current behavior), this compiles:<br>
<br>
ptr = { writeln("hello!"); };<br>
<br>
<br>
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<br>
<br>
void delegate() ptr = &a.foo; // ok, use looser type<br>
ptr = {...}; // perfectly fine<br>
<br>
<br>
<br>
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.<br>
<br>
<br>
<br>
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.</blockquote>
<div><br></div><div style>I initially started with something like this. But look how much code it is! I actually deleted it all and went for my non-portable hack.</div><div style><br></div><div style>My implementation was different. You've basically wrapped up a delegate, and made something that emulates a delegate (I'm not sure why?).</div>
<div style>I don't want a delegate, I want a function pointer. So my solution was similar, but wrapped up a function pointer and aliased a delegate to the correct type, then created a local delegate populating with 'this' and the function at the call-site...</div>
<div style>But it's all crap! It's really just abusing a delegate to get at the concept it embed's which doesn't have a proper expression.</div></div></div></div>