www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Error: C++ base class needs at least one virtual function

reply Ethan <gooberman gmail.com> writes:
I know how this is going to get in to a discussion about how 
virtual destructors are necessary, but in this case it's not and 
I know better than the compiler. tl;dr is "How do I make the 
compiler behave the way I want it to?"

This is going to be representative of code in my runtime. Every 
object I deal with will be a reference, so classes are a logical 
choice. They'll be stored in an immutable hashmap using a unique 
ID, they have type checking built in to the ID, and my reference 
retrieval object will do the type checking and casting.

And the only reason I'm using classes? Because struct inheritance 
is still considered a monumentally bad thing despite the solid 
use cases. I know the outright hacks around it, my DConf 2016 and 
2017 talks went in to detail about it. But really, I don't want 
this code to employ such gross hacks for what should be something 
simple.

Notice I even enforce every method to be final in my derived 
class to emphasise that *I do not want nor need virtuals ever in 
this code*.

--------

extern(C++):

class Base
{
     int uniqueID;
}

class Derived : Base
{
final:
      property UniqueID( int val ) { uniqueID= val; return 
uniqueID; }
      property UniqueID() const { return uniqueID; }
}

int main( string[] args )
{
     Base obj = new Base;
     obj.Value = 42;

     import std.stdio : writeln;
     writeln( obj.Value );

     return 0;
}
Jul 27 2019
next sibling parent Ethan <gooberman gmail.com> writes:
On Saturday, 27 July 2019 at 19:58:05 UTC, Ethan wrote:
     obj.Value = 42;
     writeln( obj.Value );
(Yeah those are meant to say UniqueID)
Jul 27 2019
prev sibling next sibling parent reply Johan Engelen <j j.nl> writes:
On Saturday, 27 July 2019 at 19:58:05 UTC, Ethan wrote:
 I know how this is going to get in to a discussion about how 
 virtual destructors are necessary, but in this case it's not 
 and I know better than the compiler. tl;dr is "How do I make 
 the compiler behave the way I want it to?"
Can I rephrase your point as this? Normal D classes do not require their parent to have at least one virtual function, but extern(C++) classes do. Please remove this limitation from extern(C++) classes. -Johan
Jul 28 2019
parent Ethan <gooberman gmail.com> writes:
On Sunday, 28 July 2019 at 09:27:56 UTC, Johan Engelen wrote:
 Can I rephrase your point as this?
 Normal D classes do not require their parent to have at least 
 one virtual function, but extern(C++) classes do. Please remove 
 this limitation from extern(C++) classes.

 -Johan
That's not accurate. Normal D classes without a defined parent have Object as a parent, which defines 4 virtuals at last count. The "no virtuals" template in my code that static asserts when a virtual is detected specifically ignores Object and its virtuals (and also static asserts if a user overrides those virtuals). If I said I wanted the limitation removed coming in, the obvious counterpoint is "But memory safety is thrown out the window without virtual destructors so we absolutely should enforce this." To which I'd have nothing except that I don't need it in my use case. I'm not asking to remove the limitation. I'm asking for an option to disable the error because I have a valid use case for it and I know what I'm doing.
Jul 28 2019
prev sibling parent Ethan <gooberman gmail.com> writes:
On Saturday, 27 July 2019 at 19:58:05 UTC, Ethan wrote:
 tl;dr is "How do I make the compiler behave the way I want it 
 to?"
Write the code myself of course. I've got a solution working locally. And looking at the implementation that was already there... I'm not going to submit a pull request just yet. The code only checks for the existence of virtuals, it doesn't care if one of those virtuals is a destructor or not. This isn't good enough as I see it. So I'm going to think about how best to tackle that. But hey, my code compiles and executes perfectly now that I have a solution. There is *one* thing I'm going to get a pull request ready for however Compile this code using a Windows DMD: ---------- extern( C++ ) class SomeClass { property someString() const { return "Wahoo"; } } ---------- Compiler output: Internal Compiler Error: type `string` can not be mapped to C++ ---------- This is a Windows only problem. Linux builds have no issue with slices of any kind. So that's one problem that I'm going to analyse and fix in another pull request. Probably requires the codegen to define a virtual Slice struct knowing how Windows works. But what I'm interested in is that ICE. So here's the thing with every ICE in cppmanglewin.d. Every single one of them is as the result of a user error. As near as I can tell, an ICE is raised because it wants compilation to just plain stop and go no further. It's not an unexpected state, it's a state it knows the user can input but compilation past that point is plain wrong as far as it's concerned. And yet none of these ICE give the file and line number in the log. "So just go through your extern( C++ ) objects and look for-" Yeah, nah, how about I get a file and line in the output that I can double click on in my IDE (it's Windows so of course I mean Visual Studio) and go straight to the problem. Massive time saver. That change I've got there is quite self contained, so a pull request will be incoming for that.
Jul 28 2019