Thread overview
Virtual methods and class libraries
Apr 19, 2004
Mike Hearn
Apr 19, 2004
Walter
Apr 20, 2004
Alexander Larsson
Apr 20, 2004
Andy Friesen
Apr 21, 2004
Mike Hearn
Apr 22, 2004
Andy Friesen
April 19, 2004
Hi,

The D spec says:

"All non-static non-private member functions are virtual. This may sound inefficient, but since the D compiler knows all of the class hierarchy when generating code, all functions that are not overridden can be optimized to be non-virtual."

... but I don't understand how this works when you wish to place objects into shared libraries. In this case, it might be the case that another library subclasses the class and therefore the compiler cannot know the entire class hierarchy.

How is this supposed to work? I know D has no stable ABI, but this could be a serious problem for people wishing to split programs into multiple DSOS/DLLS.

If this situation simply is not dealt with currently, a virtual keyword that forces a method to have a vtable entry may be a useful addition to the language.

thanks -mike
April 19, 2004
"Mike Hearn" <mike@navi.cx> wrote in message news:pan.2004.04.19.13.51.27.807711@navi.cx...
> Hi,
>
> The D spec says:
>
> "All non-static non-private member functions are virtual. This may sound inefficient, but since the D compiler knows all of the class hierarchy when generating code, all functions that are not overridden can be optimized to be non-virtual."
>
> ... but I don't understand how this works when you wish to place objects into shared libraries. In this case, it might be the case that another library subclasses the class and therefore the compiler cannot know the entire class hierarchy.

You're right. For those cases, a virtual call will be made. And, even if a non-virtual call can be made, the compiler still puts it in the vtbl[].


April 20, 2004
On Mon, 19 Apr 2004 16:51:50 -0700, Walter wrote:

> 
> "Mike Hearn" <mike@navi.cx> wrote in message news:pan.2004.04.19.13.51.27.807711@navi.cx...
>> Hi,
>>
>> The D spec says:
>>
>> "All non-static non-private member functions are virtual. This may sound inefficient, but since the D compiler knows all of the class hierarchy when generating code, all functions that are not overridden can be optimized to be non-virtual."
>>
>> ... but I don't understand how this works when you wish to place objects into shared libraries. In this case, it might be the case that another library subclasses the class and therefore the compiler cannot know the entire class hierarchy.
> 
> You're right. For those cases, a virtual call will be made. And, even if a non-virtual call can be made, the compiler still puts it in the vtbl[].

I still don't get it. The typical use case here would be writing a shared
library for e.g. a gui, where the app using the library typically derive
from classes in the library. Now, when building the library, how does the
compiler know that it has to make virtual calls, even though no class
in the library overrides the method, because some app code might
derive from the class and override the method?
April 20, 2004
Alexander Larsson wrote:
> I still don't get it. The typical use case here would be writing a shared
> library for e.g. a gui, where the app using the library typically derive
> from classes in the library. Now, when building the library, how does the
> compiler know that it has to make virtual calls, even though no class
> in the library overrides the method, because some app code might
> derive from the class and override the method? 

Foo foo = new Bar();
foo.blah(); // the compiler can be absolutely certain that foo refers to a Bar in this case

 -- andy
April 21, 2004
On Tue, 20 Apr 2004 09:01:18 -0700, Andy Friesen wrote:
> Foo foo = new Bar();
> foo.blah(); // the compiler can be absolutely certain that foo refers to
> a Bar in this case

No, it still sounds broken.

class A {
	int somefunc() {
		return 1;
	}
}

extern A giveMeAnObject();

// link with a plugin that is supposed to subclass A
A a = giveMeAnObject()
a.somefun == 1; // true

------------- now in a shared library -----------------

class B : A {
	int somefunc() { return 2; }
}

A giveMeAnObject() { return new B(); }

-------------------------------------------------------

The compiler when compiling the first binary will think nothing is subclassing A, so not make the methods virtual. Now the shared library cannot subclass A and return it using polymorphism.

What am I missing here which makes this work?

thanks -mike
April 22, 2004
Mike Hearn wrote:
> On Tue, 20 Apr 2004 09:01:18 -0700, Andy Friesen wrote:
> 
>>Foo foo = new Bar();
>>foo.blah(); // the compiler can be absolutely certain that foo refers to a Bar in this case
> 
> 
> No, it still sounds broken.
> 
> class A {
> 	int somefunc() {
> 		return 1;
> 	}
> }
> 
> extern A giveMeAnObject();
> 
> // link with a plugin that is supposed to subclass A
> A a = giveMeAnObject()
> a.somefun == 1; // true
> 
> ------------- now in a shared library -----------------
> 
> class B : A {
> 	int somefunc() { return 2; }
> }
> 
> A giveMeAnObject() { return new B(); }
> 
> -------------------------------------------------------
> 
> The compiler when compiling the first binary will think nothing is
> subclassing A, so not make the methods virtual. Now the shared library
> cannot subclass A and return it using polymorphism.
> 
> What am I missing here which makes this work?

In this example, the compiler is only sure that giveMeAnObject yields an A instance. (which is enough to inline any final methods A may have, but little else)  As you said, this isn't enough for the compiler to be able to optimize the virtual call out.  Come to think of it, this would probably be enough for the compiler if A was declared as a final class.

In my example, the new operator was used directly, so the compiler had unambigious knowledge of the exact type of the object.

The trick is that the D compiler is optimizing specific method calls, not marking a given method as being inlineable or not.

 -- andy