www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Why can't a method be virtual AND static at the same time?

reply "Martin Cejp" <minexew gmail.com> writes:
This is a feature I've always missed in C++. Consider the code
below:

import std.stdio;

interface Logger {
	void print(string msg);
}

class ConsoleLogger : Logger {
	static override void print(string msg) {
		writeln(msg);
	}
}

void main() {
	Logger logger = new ConsoleLogger;
	
	ConsoleLogger.print("Hello, World!");
}

Such definition of ConsoleLogger fails to compile. I don't see
any drawbacks to allowing this though, except the compiler would
probably have to generate 2 methods internally.
The way it is now, you have to either define each method twice or
always create an instance.
Or am I missing an obvious solution?
Jan 29 2014
next sibling parent "Vladimir Panteleev" <vladimir thecybershadow.net> writes:
On Wednesday, 29 January 2014 at 13:30:54 UTC, Martin Cejp wrote:
 This is a feature I've always missed in C++. Consider the code
 below:

 import std.stdio;

 interface Logger {
 	void print(string msg);
 }

 class ConsoleLogger : Logger {
 	static override void print(string msg) {
 		writeln(msg);
 	}
 }

 void main() {
 	Logger logger = new ConsoleLogger;
 	
 	ConsoleLogger.print("Hello, World!");
 }

I don't understand this code. You have a "logger" local variable, but don't use it. Do you want the compiler to autodetect which local variable to use by selecting one in the current scope?
 Such definition of ConsoleLogger fails to compile. I don't see
 any drawbacks to allowing this though, except the compiler would
 probably have to generate 2 methods internally.
 The way it is now, you have to either define each method twice 
 or
 always create an instance.
 Or am I missing an obvious solution?

What are you trying to accomplish? You can use object instances as namespaces, too. Example: ///////////////////////////////////////////////////////// import std.stdio; interface Logger { void print(string msg); } class ConsoleLoggerImpl : Logger { override void print(string msg) const { writeln(msg); } } const ConsoleLogger = new ConsoleLoggerImpl; void main() { ConsoleLogger.print("Hello, World!"); } ///////////////////////////////////////////////////////// Note that ConsoleLogger is "instantiated" at compile time, not runtime. To instantiate it at runtime, during initialization, you can use a static constructor.
Jan 29 2014
prev sibling next sibling parent "Martin Cejp" <minexew gmail.com> writes:
On Wednesday, 29 January 2014 at 13:39:24 UTC, Vladimir Panteleev 
wrote:
 On Wednesday, 29 January 2014 at 13:30:54 UTC, Martin Cejp 
 wrote:
 This is a feature I've always missed in C++. Consider the code
 below:

 import std.stdio;

 interface Logger {
 	void print(string msg);
 }

 class ConsoleLogger : Logger {
 	static override void print(string msg) {
 		writeln(msg);
 	}
 }

 void main() {
 	Logger logger = new ConsoleLogger;
 	
 	ConsoleLogger.print("Hello, World!");
 }

I don't understand this code. You have a "logger" local variable, but don't use it. Do you want the compiler to autodetect which local variable to use by selecting one in the current scope?

Oh no, these are supposed to be 2 independent usage examples, 1) being able to pass an instance of ConsoleLogger to a function/class expecting Logger and 2) being able to call a method without any instance at all.
 Such definition of ConsoleLogger fails to compile. I don't see
 any drawbacks to allowing this though, except the compiler 
 would
 probably have to generate 2 methods internally.
 The way it is now, you have to either define each method twice 
 or
 always create an instance.
 Or am I missing an obvious solution?

What are you trying to accomplish? You can use object instances as namespaces, too. Example: ///////////////////////////////////////////////////////// import std.stdio; interface Logger { void print(string msg); } class ConsoleLoggerImpl : Logger { override void print(string msg) const { writeln(msg); } } const ConsoleLogger = new ConsoleLoggerImpl; void main() { ConsoleLogger.print("Hello, World!"); } ///////////////////////////////////////////////////////// Note that ConsoleLogger is "instantiated" at compile time, not runtime. To instantiate it at runtime, during initialization, you can use a static constructor.

At compile time? Wow. Well, I guess this might work for me then :) (though there's no way around having to use 2 different names for the class and the const instance, is there?)
Jan 29 2014
prev sibling next sibling parent "Tobias Pankrath" <tobias pankrath.net> writes:
 Such definition of ConsoleLogger fails to compile. I don't see
 any drawbacks to allowing this though, except the compiler would
 probably have to generate 2 methods internally.
 The way it is now, you have to either define each method twice 
 or
 always create an instance.
 Or am I missing an obvious solution?

Instead of defining it twice, you could alias the base version.
Jan 29 2014
prev sibling next sibling parent "Martin Cejp" <minexew gmail.com> writes:
On Wednesday, 29 January 2014 at 14:29:24 UTC, Tobias Pankrath 
wrote:
 Such definition of ConsoleLogger fails to compile. I don't see
 any drawbacks to allowing this though, except the compiler 
 would
 probably have to generate 2 methods internally.
 The way it is now, you have to either define each method twice 
 or
 always create an instance.
 Or am I missing an obvious solution?

Instead of defining it twice, you could alias the base version.

What do you mean? The methods have different signatures (static x virtual) By the way, what I want to do is have a class LocalFileSystem that can both be passed as a FileSystem implementation and used directly, without instantiation (LocalFileSystem.openFile)
Jan 29 2014
prev sibling next sibling parent "Frustrated" <c1514843 drdrb.com> writes:
On Wednesday, 29 January 2014 at 13:30:54 UTC, Martin Cejp wrote:
 This is a feature I've always missed in C++. Consider the code
 below:

 import std.stdio;

 interface Logger {
 	void print(string msg);
 }

 class ConsoleLogger : Logger {
 	static override void print(string msg) {
 		writeln(msg);
 	}
 }

 void main() {
 	Logger logger = new ConsoleLogger;
 	
 	ConsoleLogger.print("Hello, World!");
 }

 Such definition of ConsoleLogger fails to compile. I don't see
 any drawbacks to allowing this though, except the compiler would
 probably have to generate 2 methods internally.
 The way it is now, you have to either define each method twice 
 or
 always create an instance.
 Or am I missing an obvious solution?

No, you are mixing concepts. Logger.print is a function that uses a vtable and takes a hidden parameter this. ConsoleLogger.print is a regular function that is not in a vtable(because it is static) and does not take a hidden parameter(again, because it is static). So, when you do ConsoleLogger.print how the heck would the compiler know which one you meant to use? While this might have a solution, in some cases in general a virtual method REQUIRES a virtual table and by using the keyword static on a function you are saying it is not part of the virtual table. Basically what you want is polymorphism here. Your two print functions are different. module main; import std.stdio; interface Logger { void print(string msg); } class ConsoleLogger : Logger { void print(string msg) { writeln(msg); } static void print2(string msg) { writeln(msg); } // Can we not rename print2 to print? } void main() { Logger logger = new ConsoleLogger; ConsoleLogger.print2("Hello, World!"); } works because you used a different name for print(print2) so there is no confusion. One could say that since you "access" print from the class(and not the object) that the compiler should use the static method instead of the virtual. This is actually different than saying print is both static and non-static(virtual), which is impossible. This all has to do with using the the same name of different things just like overriding methods do(they are actually different methods but use the same name because they have different definitions and generally can be deduced). It may be possible to do the same here. I don't know if there are any pitfalls. You would still need to implement the interface method print though, regardless. The real issue comes from cases like this though: module main; import std.stdio; interface Logger { void print(string msg); } class ConsoleLogger : Logger { void print(string msg) { writeln(msg); } static void print(string msg) { writeln(msg); } // assume this works } void main() { auto logger = new ConsoleLogger; logger.print("Hello, World!"); } Do you know the issue now? (Again, there might be ways around it but current that is not how the compiler thinks)
Jan 29 2014
prev sibling next sibling parent "Martin Cejp" <minexew gmail.com> writes:
On Wednesday, 29 January 2014 at 15:21:54 UTC, Frustrated wrote:
 No, you are mixing concepts. Logger.print is a function that 
 uses
 a vtable and takes a hidden parameter this.

 ConsoleLogger.print is a regular function that is not in a
 vtable(because it is static) and does not take a hidden
 parameter(again, because it is static).

 So, when you do ConsoleLogger.print how the heck would the
 compiler know which one you meant to use? While this might have 
 a
 solution, in some cases in general a virtual method REQUIRES a
 virtual table and by using the keyword static on a function you
 are saying it is not part of the virtual table.

No really, I don't care at all about the implementation details. If you look at it from the purely semantic view, there's no ambiguity: got an instance? call the instance method. no instance? call the static one. Technically, yes, there would need to be two methods generated because of ABI differences, but this could be done behind the scenes. By making a method both override and static, you'd tell the compiler to do exactly that. Of course, the question is whether this would really be worth implementing and based on the reactions so far, I guess the answer is Not at all. I'm surprised that nobody else misses this feature, though.
Jan 29 2014
prev sibling next sibling parent "Dicebot" <public dicebot.lv> writes:
"Explicit better than implicit"
Jan 29 2014
prev sibling next sibling parent "Frustrated" <c1514843 drdrb.com> writes:
On Wednesday, 29 January 2014 at 15:30:38 UTC, Martin Cejp wrote:
 On Wednesday, 29 January 2014 at 15:21:54 UTC, Frustrated wrote:
 No, you are mixing concepts. Logger.print is a function that 
 uses
 a vtable and takes a hidden parameter this.

 ConsoleLogger.print is a regular function that is not in a
 vtable(because it is static) and does not take a hidden
 parameter(again, because it is static).

 So, when you do ConsoleLogger.print how the heck would the
 compiler know which one you meant to use? While this might 
 have a
 solution, in some cases in general a virtual method REQUIRES a
 virtual table and by using the keyword static on a function you
 are saying it is not part of the virtual table.

No really, I don't care at all about the implementation details. If you look at it from the purely semantic view, there's no ambiguity: got an instance? call the instance method. no instance? call the static one. Technically, yes, there would need to be two methods generated because of ABI differences, but this could be done behind the scenes. By making a method both override and static, you'd tell the compiler to do exactly that. Of course, the question is whether this would really be worth implementing and based on the reactions so far, I guess the answer is Not at all. I'm surprised that nobody else misses this feature, though.

I don't care either. It's not that I don't disagree with you but I simply no longer care any more. I would care, and assuming such a thing mattered then I could care. I just don't any more....
Jan 29 2014
prev sibling next sibling parent "ed" <growlercab gmail.com> writes:
On Wednesday, 29 January 2014 at 15:30:38 UTC, Martin Cejp wrote:

You cannot override the static method because it isn't in the 
vtable and has a different calling convention; no this pointer is 
passed.

 Technically, yes, there would need to be two methods generated 
 because of ABI differences, but this could be done behind the 
 scenes. By making a method both override and static, you'd tell 
 the compiler to do exactly that. Of course, the question is 
 whether this would really be worth implementing and based on 
 the reactions so far, I guess the answer is Not at all. I'm 
 surprised that nobody else misses this feature, though.

So you want to overload the static method. Well you can do that (untested but should work): --- class A { static void f() {writeln(__PRETTY_FUNCTION__);} } class B : A { void f() {writeln(__PRETTY_FUNCTION__);} } class C : B { // Overrides from B, overloads from A override void f() {writeln(__PRETTY_FUNCTION__);} } void main () { auto b= new B(); auto c = new C(); A.f(); b.f(); c.f(); } --- Is this what you're trying to do? IMO though overloading base class methods should be avoided Cheers, ed
Jan 29 2014
prev sibling next sibling parent "Vladimir Panteleev" <vladimir thecybershadow.net> writes:
On Wednesday, 29 January 2014 at 14:24:12 UTC, Martin Cejp wrote:
 (though there's no way around having to use 2 different names 
 for the class and the const instance, is there?)

Actually, there is: http://dlang.org/class.html#anonymous But I'm not sure if it works at module level.
Jan 29 2014
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Wed, 29 Jan 2014 08:30:53 -0500, Martin Cejp <minexew gmail.com> wrote:

 This is a feature I've always missed in C++. Consider the code
 below:

 import std.stdio;

 interface Logger {
 	void print(string msg);
 }

 class ConsoleLogger : Logger {
 	static override void print(string msg) {
 		writeln(msg);
 	}
 }

 void main() {
 	Logger logger = new ConsoleLogger;
 	
 	ConsoleLogger.print("Hello, World!");
 }

 Such definition of ConsoleLogger fails to compile. I don't see
 any drawbacks to allowing this though, except the compiler would
 probably have to generate 2 methods internally.
 The way it is now, you have to either define each method twice or
 always create an instance.
 Or am I missing an obvious solution?

Interface Logger { final void print(string msg) { printImpl(msg); } void printImpl(string msg); } class ConsoleLogger : Logger { static void print(string msg) { writeln(msg); } override void printImpl(string msg) { print(msg); } } The issue you have is with the naming, you can't overload a virtual function with a static one. A static function call is a different call than a virtual one. You can't mix the two. -Steve
Jan 30 2014
prev sibling next sibling parent "Vitali" <notavailable mail.com> writes:
Your motivation for this question is not clear. I can only guess.

If you hope to get more performance by providing only one
function, which is used as virtual and static at the same time,
then this is impossible, because virtual functions are bound to a
data instance by design.

If you look for a possibility to type less code by skipping the
instantiation of a class, then the use of an interface may be the
wrong decision.

If you want the virtual function to behave the same as the static
function, then you can wrap the static function with the virtual
one, like this:

class ConsoleLogger : Logger {
    void print(string msg) {
      staticPrint(msg);
    }
    static void staticPrint(string msg) {
      writeln(msg);
    }
}

If you look for a possibility to override a static function like
a virtual one, then you may be interested in so called "system
mixins" (not part of D). System mixins are researched in aspect
oriented programming (AOP). Read about it in this paper:
Ichisugi, Y., Roudier, Y. (1998) Mixin Composition Strategies for
the Modular
Implementation of Aspect Weaving, Kyoto: Aspect Oriented
Programming workshop at
ICSE'98
Jan 30 2014
prev sibling parent "ed" <growlercab gmail.com> writes:
On Thursday, 30 January 2014 at 13:24:09 UTC, Steven 
Schveighoffer wrote:
[snip]
 The issue you have is with the naming, you can't overload a 
 virtual function with a static one. A static function call is a 
 different call than a virtual one. You can't mix the two.

 -Steve

Actually you can do this, see my other post. Overloading a static method with a virtual method or vice-versa works fine. You cannot do is override a static method because it isn't virtual and is nonsensical IMO. The OP said they expected overloading to occur in response to another post: [snip]
 What do you mean? The methods have different signatures (static 
 x virtual)

so I don't know why they were thinking to override, probably just a simple mistake. Cheers, ed
Jan 30 2014