View Issue Details
| ID | Project | Category | View Status | Date Submitted | Last Update |
|---|---|---|---|---|---|
| 0000889 | LDMud 3.6 | LPC Compiler/Preprocessor | public | 2021-04-07 22:01 | 2021-04-08 15:37 |
| Reporter | realms-mud | Assigned To | |||
| Priority | normal | Severity | block | Reproducibility | always |
| Status | closed | Resolution | won't fix | ||
| Product Version | 3.6.4 | ||||
| Summary | 0000889: Nested virtual inheritance broken | ||||
| Description | If you have a program C that inherits program B that, in turn, inherits program A and attempts to explicitly call a function from program A in program C, it will no longer compile. Trying to do this will now fail to compile with a "function not defined by inheritance as specified before ';'." error. | ||||
| Steps To Reproduce | Use this example: //////////////////////// Program A ///////////////////////////////////// public void A() { printf("A() called\n"); } //////////////////////// Program B ///////////////////////////////////// virtual inherit "/a.c"; public void B() { printf("B() called\n"); } //////////////////////// Program C ///////////////////////////////////// virtual inherit "/b.c"; public void A() { a::A(); } If you attempt to compile program C, it will now fail. | ||||
| Tags | No tags attached. | ||||
|
|
Hmm, why should that work? Why should C be allowed to circumvent B and call directly into B's inherits? With non-virtual inherits this was never allowed. For virtual inherits, the way they were implemented did not prevent it. But the validity of an inherited function call should imho not depend on a virtual modifier, therefore the behavior in both cases is now the same. Is there any reason why a program like C should be okay? |
|
|
What I showed is basically the whole point behind polymorphism as an object-oriented construct and is widely used throughout the industry. A is an object with a method on it. B is based off of A (ie: B isn't a separate "thing", it's actually B + A). C inherits B (IE: the meta "B + A" object) and overloads one of the methods, it's coincidental that B's A() method is actually originating from its parent. The object is the sum of all of its methods from all the sources it came from and you can overload any of them. This is somewhat the foundation upon which large frameworks like Qt and .NET are built. Let's say that you want to create a new container class based off of .NET's Queue. You can implement your own methods, overload things from Queue, overload things from Object (or the Enumerator class, etc) while still being able to call Object's implementation "as a starting point" or what have you. That's exactly what I'm doing here. I can whip up a class diagram to show what I'm doing if you'd like. The work-around for something like this either introduces a bad design (create an intermediary method in A, have A's implementation of A() call it, and then in C's overload of A(), call that intermediary and do "other stuff" that was the whole point of the overload) OR break encapsulation by giving C access to A's data and re-implementing the method in question. Neither is a good alternative to just being able to do something that is a standard OO construct. |
|
|
Zesstra just told me that I can call B::A() in C and everything will work. I did not even think to try that. My apologies. Duh! |
|
|
Oh, I'm sorry I didn't mention that. I thought this is about circumventing B. A's functions are part of B and B can use them as they are or override them. So you can call b::A() and either get the original or an override. But I thought this is about ignoring any overrides in B and directly calling into A, to make sure you get the original. I looked into other languages: C++ allows that, as long as B inherits A publicly. Java prohibits that. In LPC this was allowed for virtual inherits and prohibited for non-virtual ones. As the virtual modifier is not an accessibility modifier, the behavior should be the same in both cases. Therefore it is prohibited in both cases, now. Admittedly this is based on a personal opinion, that you should not work around an inherited program, but that's why I asked for reasons, why it should be allowed. |
|
|
Clearly, I've spent too much time in the C++ (and C#) worlds lately - they've rotted my brain. I admit that I've had cause to do that "circumvent" thing in C++ before, but I don't need to do anything like that with what I've (ever) written in LPC and can certainly live with not being allowed to. Usually when a scenario like that comes up where you have to circumvent something like that, you're doing it to get around a bad design. In C++, using "virtual" changes the vtable so that you're guaranteed to not be able to inadvertently call the base's method if, for example, you've got that pointer to the base. The reason I never noticed the issue before is because I am using the virtual inherits for legitimate reasons - multiple inherited things having the same base - not to get around this issue. I do share your opinion - virtual shouldn't be allowed to be used as an accessibility modifier. Thanks for the quick response and getting me on the right track! I really appreciate all the work that you guys have been doing. I think this issue can be safely closed. |
| Date Modified | Username | Field | Change |
|---|---|---|---|
| 2021-04-07 22:01 | realms-mud | New Issue | |
| 2021-04-07 22:56 | Gnomi | Note Added: 0002566 | |
| 2021-04-08 08:27 | Gnomi | Status | new => feedback |
| 2021-04-08 14:33 | realms-mud | Note Added: 0002567 | |
| 2021-04-08 14:33 | realms-mud | Status | feedback => new |
| 2021-04-08 14:38 | realms-mud | Note Added: 0002568 | |
| 2021-04-08 14:57 | Gnomi | Note Added: 0002569 | |
| 2021-04-08 15:32 | realms-mud | Note Added: 0002570 | |
| 2021-04-08 15:37 | Gnomi | Status | new => closed |
| 2021-04-08 15:37 | Gnomi | Resolution | open => won't fix |