Thread overview
Virtual methods on stack objects
May 11, 2022
Marvin Hannott
May 11, 2022
Ali Çehreli
May 11, 2022
Marvin Hannott
May 11, 2022
Ali Çehreli
May 11, 2022
Marvin Hannott
May 12, 2022
dangbinghoo
May 12, 2022
vit
May 11, 2022

Hi everybody!

I understand that D's classes solve some particular problems. However, they also cause problems because they cannot be copied when scoped. So how do I get virtual methods on a copyable stack object?

May 11, 2022
On 5/11/22 06:57, Marvin Hannott wrote:

> I understand that D's classes solve some particular problems. However,
> they also cause problems because they cannot be copied when scoped. So
> how do I get virtual methods on a copyable stack object?

Problems classes solve don't usually need copying nor need to be on the stack. Is that really necessary?

If so, I can think of two solutions:

a) Use a struct but provide "virtual" functions manually (with function pointers like one would do in C, all of which potentially collected in a per-type function table (a.k.a. vtbl :) )).

b) Use classes but provide a virtual function that you call to copy.

Ali

May 11, 2022

On Wednesday, 11 May 2022 at 14:44:59 UTC, Ali Çehreli wrote:

>

On 5/11/22 06:57, Marvin Hannott wrote:

>

I understand that D's classes solve some particular problems.
However,
they also cause problems because they cannot be copied when
scoped. So
how do I get virtual methods on a copyable stack object?

Problems classes solve don't usually need copying nor need to be on the stack. Is that really necessary?

If so, I can think of two solutions:

a) Use a struct but provide "virtual" functions manually (with function pointers like one would do in C, all of which potentially collected in a per-type function table (a.k.a. vtbl :) )).

b) Use classes but provide a virtual function that you call to copy.

Ali

I appreciate the answer, don't much like the "solutions".

It's not so much about copying, but about heap allocations. Tying virtual methods to heap allocation is kind of unfortunate. And I am not really keen on building a poor man's vtbl.
And for my taste, scoped class objects should have value semantics.

May 11, 2022
On 5/11/22 13:06, Marvin Hannott wrote:

> I appreciate the answer, don't much like the "solutions".

Me neither. :)

> It's not so much about copying

Great!

> scoped class objects should have value semantics.

std.typecons.scoped does exactly that:

  https://dlang.org/phobos/std_typecons.html#scoped

import std.stdio;
import std.typecons;

interface Animal {
  string sing();
}

class Cat : Animal {
  this() {
    writeln("Hi!");
  }

  ~this() {
    writeln("Bye!");
  }

  string sing() {
    return "mew";
  }
}

void foo(Animal animal) {
  writeln(animal.sing());
}

void bar() {
  auto cat = scoped!Cat();
  foo(cat);
}

void main() {
  bar();
}

The output:

Hi!
mew
Bye!

Ali

May 11, 2022

On Wednesday, 11 May 2022 at 20:23:07 UTC, Ali Çehreli wrote:

>

On 5/11/22 13:06, Marvin Hannott wrote:

>

I appreciate the answer, don't much like the "solutions".

Me neither. :)

>

It's not so much about copying

Great!

>

scoped class objects should have value semantics.

std.typecons.scoped does exactly that:

https://dlang.org/phobos/std_typecons.html#scoped

import std.stdio;
import std.typecons;

interface Animal {
string sing();
}

class Cat : Animal {
this() {
writeln("Hi!");
}

~this() {
writeln("Bye!");
}

string sing() {
return "mew";
}
}

void foo(Animal animal) {
writeln(animal.sing());
}

void bar() {
auto cat = scoped!Cat();
foo(cat);
}

void main() {
bar();
}

The output:

Hi!
mew
Bye!

Ali

Yeah, but you can't return Cat 😉. And the documentation for scoped says:

>

It's illegal to move a class instance even if you are sure there are no pointers to it. As such, it is illegal to move a scoped object.

That's kinda very limiting.

Anyway, I cooked up another idea based on your first suggestions.

struct S
{
    static private interface I
    {
        int f(int i);
    }

    static private final class A : I
    {
        int f(int i) {return i;}
    }

    static private final class B : I
    {
        int f(int i) {return i+ 1;}
    }

    private I i;
    private int d;

    this(int d)
    {
        this.d = d;
        if(d < 10)
        {
            i = scoped!A();
        }else {
            i = scoped!B();
        }
    }

    int f() { return i.f(d);}
}

I mean, this is a super dumb example, but it kinda works. And I think it could be made a lot less tedious with some mixin magic.

May 12, 2022

On Wednesday, 11 May 2022 at 20:53:21 UTC, Marvin Hannott wrote:

>

On Wednesday, 11 May 2022 at 20:23:07 UTC, Ali Çehreli wrote:

>

[...]

Yeah, but you can't return Cat 😉. And the documentation for scoped says:

>

[...]

That's kinda very limiting.

Anyway, I cooked up another idea based on your first suggestions.

struct S
{
    static private interface I
    {
        int f(int i);
    }

    static private final class A : I
    {
        int f(int i) {return i;}
    }

    static private final class B : I
    {
        int f(int i) {return i+ 1;}
    }

    private I i;
    private int d;

    this(int d)
    {
        this.d = d;
        if(d < 10)
        {
            i = scoped!A();
        }else {
            i = scoped!B();
        }
    }

    int f() { return i.f(d);}
}

I mean, this is a super dumb example, but it kinda works. And I think it could be made a lot less tedious with some mixin magic.

add a single writeln("A.f") to A.f and writeln("B.f") B.f, and a simple test

    void main()
    {
        S s1 = S(9);
        S s2 = S(12);
        s1.f();
        s2.f();
    }

outputs:

A.f
Error: program killed by signal 11
(Aka.  segmentation fault !)
May 12, 2022

On Wednesday, 11 May 2022 at 20:53:21 UTC, Marvin Hannott wrote:

>

On Wednesday, 11 May 2022 at 20:23:07 UTC, Ali Çehreli wrote:

>

On 5/11/22 13:06, Marvin Hannott wrote:

>

I appreciate the answer, don't much like the "solutions".

Me neither. :)

>

It's not so much about copying

Great!

>

scoped class objects should have value semantics.

std.typecons.scoped does exactly that:

https://dlang.org/phobos/std_typecons.html#scoped

import std.stdio;
import std.typecons;

interface Animal {
string sing();
}

class Cat : Animal {
this() {
writeln("Hi!");
}

~this() {
writeln("Bye!");
}

string sing() {
return "mew";
}
}

void foo(Animal animal) {
writeln(animal.sing());
}

void bar() {
auto cat = scoped!Cat();
foo(cat);
}

void main() {
bar();
}

The output:

Hi!
mew
Bye!

Ali

Yeah, but you can't return Cat 😉. And the documentation for scoped says:

>

It's illegal to move a class instance even if you are sure there are no pointers to it. As such, it is illegal to move a scoped object.

That's kinda very limiting.

Anyway, I cooked up another idea based on your first suggestions.

struct S
{
    static private interface I
    {
        int f(int i);
    }

    static private final class A : I
    {
        int f(int i) {return i;}
    }

    static private final class B : I
    {
        int f(int i) {return i+ 1;}
    }

    private I i;
    private int d;

    this(int d)
    {
        this.d = d;
        if(d < 10)
        {
            i = scoped!A();
        }else {
            i = scoped!B();
        }
    }

    int f() { return i.f(d);}
}

I mean, this is a super dumb example, but it kinda works. And I think it could be made a lot less tedious with some mixin magic.

scoped is not necesary, try this:

import std.typecons;

    struct S{
        static private interface I{
            int f(int i)const;
        }

        static private final class A : I{
            static immutable typeof(this) instance = new immutable typeof(this)();

            int f(int i)const {return i;}
        }

        static private final class B : I{
            static immutable typeof(this) instance = new immutable typeof(this)();

            int f(int i)const {return i+ 1;}
        }

        private immutable I i;
        private int d;

        this(int d){
            this.d = d;

            if(d < 10) i = A.instance;
            else i = B.instance;

        }

        int f() { return i.f(d);}
    }

    void main(){
    	import std.stdio;
    	S(1).f().writeln();  /// 1
    	S(100).f().writeln(); /// 101

}