Thread overview
Virtual methods on stack objects
6 days ago
vit
May 11

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
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

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
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

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

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 !)
6 days ago

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

}