Jump to page: 1 2
Thread overview
Make `& Class.foo` illegal
Aug 27, 2020
Johan
Aug 27, 2020
Avrina
Aug 27, 2020
Nathan S.
Aug 27, 2020
H. S. Teoh
Aug 27, 2020
Johan
Aug 28, 2020
sarn
Aug 28, 2020
Avrina
Aug 28, 2020
Johan
Aug 28, 2020
H. S. Teoh
Aug 27, 2020
JN
Aug 27, 2020
Adam D. Ruppe
Aug 27, 2020
Boris Carvajal
Aug 28, 2020
Jacob Carlborg
August 27, 2020
Currently this code is not rejected by the compiler and instead creates non-functioning (undefined) code:
```
struct S {
    void foo() {}
}

void main() {
    auto a = &S.foo;
}
```

See https://issues.dlang.org/show_bug.cgi?id=21195 .

Considering https://www.digitalmars.com/articles/b68.html, why not simply reject `&S.foo` during semantic analysis?

-Johan

August 27, 2020
On Thursday, 27 August 2020 at 11:20:17 UTC, Johan wrote:
> Currently this code is not rejected by the compiler and instead creates non-functioning (undefined) code:
> ```
> struct S {
>     void foo() {}
> }
>
> void main() {
>     auto a = &S.foo;
> }
> ```
>
> See https://issues.dlang.org/show_bug.cgi?id=21195 .
>
> Considering https://www.digitalmars.com/articles/b68.html, why not simply reject `&S.foo` during semantic analysis?
>
> -Johan

This gets brought up often. The last issue was closed as invalid. This will likely never get fixed, the issue is that it is return a function type, when it should be a delegate type with a null "this" ptr.

"Just use @safe"

https://issues.dlang.org/show_bug.cgi?id=3720#c16
August 27, 2020
On Thursday, 27 August 2020 at 11:20:17 UTC, Johan wrote:
> Currently this code is not rejected by the compiler and instead creates non-functioning (undefined) code:
> ```
> struct S {
>     void foo() {}
> }
>
> void main() {
>     auto a = &S.foo;
> }
> ```
>
> See https://issues.dlang.org/show_bug.cgi?id=21195 .
>
> Considering https://www.digitalmars.com/articles/b68.html, why not simply reject `&S.foo` during semantic analysis?
>
> -Johan

I sometimes compare member functions addresses to see if a class overrides a certain function. For example:

---
template hasDefaultOpEquals(C) if (is(C == class))
{
    enum hasDefaultOpEquals = &C.opEquals is &Object.opEquals;
}

class C1 {}

class C2 {
    int value;
    override bool opEquals(const Object rhs) const @safe {
        if (auto o = cast(typeof(this)) rhs) return value == o.value;
        return false;
    }
}

static assert(hasDefaultOpEquals!C1);
static assert(!hasDefaultOpEquals!C2);
---

August 27, 2020
On Thu, Aug 27, 2020 at 11:20:17AM +0000, Johan via Digitalmars-d wrote:
> Currently this code is not rejected by the compiler and instead
> creates non-functioning (undefined) code:
> ```
> struct S {
>     void foo() {}
> }
> 
> void main() {
>     auto a = &S.foo;
> }
> ```
> 
> See https://issues.dlang.org/show_bug.cgi?id=21195 .
> 
> Considering https://www.digitalmars.com/articles/b68.html, why not simply reject `&S.foo` during semantic analysis?
[...]

In C++, there's this construct called a member function pointer, which has its own special type and requires the caller to specify an object before the function can be called.  Arguably, that's what D should be implementing.

If we have no intention of implementing member function pointers, then this construct should indeed be illegal, or otherwise return void* so that you cannot accidentally dereference it.

I thought about making it return a function pointer with S* as the first parameter, but as Kinke pointed out in the bugnotes, the base object in a method call is treated specially and cannot be generally assumed to be the first argument to a function. So this will require a special function pointer type, or be forced to void* so that comparisons work but you can't actually call it. (Which TBH makes little sense; if we're going to allow &S.foo at all, we should do it in a thorough way and implement member function pointers properly, instead of doing a half-assed job with void*.)


T

-- 
Never step over a puddle, always step around it. Chances are that whatever made it is still dripping.
August 27, 2020
On Thursday, 27 August 2020 at 16:30:03 UTC, H. S. Teoh wrote:
> [...]

Fully agree with what you wrote.

August 27, 2020
On Thursday, 27 August 2020 at 11:20:17 UTC, Johan wrote:
> Currently this code is not rejected by the compiler and instead creates non-functioning (undefined) code:
> ```
> struct S {
>     void foo() {}
> }
>
> void main() {
>     auto a = &S.foo;
> }
> ```
>
> See https://issues.dlang.org/show_bug.cgi?id=21195 .
>
> Considering https://www.digitalmars.com/articles/b68.html, why not simply reject `&S.foo` during semantic analysis?
>
> -Johan

What if you want to pass a static member function to a C function as callback, wouldn't &S.foo be a valid syntax? Something like:

class Mouse
{
  extern "C" static void handleClick(int x, int y) { ... }
}

osAbstractionLibrary.setMouseClickHandler(&Mouse.handleClick)?

August 27, 2020
On Thursday, 27 August 2020 at 20:48:01 UTC, JN wrote:
> What if you want to pass a static member function to a C function as callback

If it is static it works fine, just non-static ones are iffy.
August 27, 2020
On Thursday, 27 August 2020 at 11:20:17 UTC, Johan wrote:
> Currently this code is not rejected by the compiler and instead creates non-functioning (undefined) code:
> struct S {
>     void foo() {}
> }
>
> void main() {
>     auto a = &S.foo;
> }

Currently, you need an instance to get a delegate, the context can be modified later to point to the desired instance:

auto dg = &S.init.foo;    // '&S().foo' or for classes: '&(new S()).foo'
S s;
dg.ptr = &s;
dg();
August 28, 2020
On Thursday, 27 August 2020 at 16:30:03 UTC, H. S. Teoh wrote:
> In C++, there's this construct called a member function pointer, which has its own special type and requires the caller to specify an object before the function can be called.  Arguably, that's what D should be implementing.

I don't know if you've seen them before, but these two articles explain the reasoning behind D not having member function pointers:
https://www.drdobbs.com/cpp/member-function-pointers-in-d/231600610
https://www.codeproject.com/Articles/7150/Member-Function-Pointers-and-the-Fastest-Possible

tl;dr: C++ member function pointers are complicated when you get into the details, and the sanest implementation is with thunks that are equivalent to lambdas or delegates.  Ironically, the most common usage of member function pointers turned out to be building complicated versions of lambdas and closures (at least before C++ got them built in).

None of that says anything about syntax, but it's why D doesn't have member function pointers, per se, like C++ does.
August 28, 2020
On Friday, 28 August 2020 at 00:25:15 UTC, sarn wrote:
> On Thursday, 27 August 2020 at 16:30:03 UTC, H. S. Teoh wrote:
>> In C++, there's this construct called a member function pointer, which has its own special type and requires the caller to specify an object before the function can be called.
>>  Arguably, that's what D should be implementing.
>
> I don't know if you've seen them before, but these two articles explain the reasoning behind D not having member function pointers:
> https://www.drdobbs.com/cpp/member-function-pointers-in-d/231600610
> https://www.codeproject.com/Articles/7150/Member-Function-Pointers-and-the-Fastest-Possible
>
> tl;dr: C++ member function pointers are complicated when you get into the details, and the sanest implementation is with thunks that are equivalent to lambdas or delegates.  Ironically, the most common usage of member function pointers turned out to be building complicated versions of lambdas and closures (at least before C++ got them built in).
>
> None of that says anything about syntax, but it's why D doesn't have member function pointers, per se, like C++ does.

D does technically have them. They just aren't type safe. As other people have said, the type should be either void*, or a delegate with a null data pointer.


import std.stdio;

struct A {
    float value;
    void foo() { writeln(this); }
}

void main() {
    void delegate() dg;

    int a = 10;

    dg.funcptr = &A.foo;
    dg.ptr = &a; // no type safety, should be A*

    dg(); // basically equivalent to C++'s member function pointer
}


With the example it makes sense why &A.foo would return a pointer and not a delegate. But because there's no special type, it incorrectly is of type `void function()` when it doesn't fit that definition.

Yes it doesn't technically need member function pointers, but the current implementation in D is broken (for many years) and not type safe like C++.

« First   ‹ Prev
1 2