www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.bugs - [Issue 4542] New: TDPL NVI example results in linker error

reply d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=4542

           Summary: TDPL NVI example results in linker error
           Product: D
           Version: D2
          Platform: x86_64
        OS/Version: Windows
            Status: NEW
          Severity: major
          Priority: P2
         Component: DMD
        AssignedTo: nobody puremagic.com
        ReportedBy: epi atari8.info


--- Comment #0 from Adrian Matoga <epi atari8.info> 2010-07-31 16:00:54 PDT ---
Trying an example from TDPL (p. 214) results in linker error.

Source:

import std.stdio;

interface Transmogrifier
{
    final void thereAndBack()
    {
        transmogrify();
        untransmogrify();
    }

private:
    void transmogrify();
    void untransmogrify();
}

class CardBoardBox : Transmogrifier
{
    override private void transmogrify()
    {
        writeln("transmogrify");
    }

    override private void untransmogrify()
    {
        writeln("untransmogrify");
    }
}

void main()
{
    (new CardBoardBox).thereAndBack();
}


Linker output:
OPTLINK (R) for Win32  Release 8.00.2
Copyright (C) Digital Mars 1989-2009  All rights reserved.
http://www.digitalmars.com/ctg/optlink.html
impl.obj(impl)
 Error 42: Symbol Undefined _D4impl14Transmogrifier12transmogrifyMFZv
impl.obj(impl)
 Error 42: Symbol Undefined _D4impl14Transmogrifier14untransmogrifyMFZv
--- errorlevel 2


The same issue occurs, when private is replaced with package.
protected and public do work.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Jul 31 2010
next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=4542


Christian Kamm <kamm-removethis incasoftware.de> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |kamm-removethis incasoftwar
                   |                            |e.de


--- Comment #1 from Christian Kamm <kamm-removethis incasoftware.de> 2010-07-31
23:11:05 PDT ---
This happens since private and package methods in D are implicitly final and
thus cannot be overridden. I'm not sure whether it is a genuine bug in TDPL or
just foreshadows to a planned change in dmd and the spec that hasn't been
applied yet.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Jul 31 2010
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=4542


Jonathan M Davis <jmdavisProg gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |jmdavisProg gmail.com


--- Comment #2 from Jonathan M Davis <jmdavisProg gmail.com> 2010-08-11
10:13:06 PDT ---
NVI is a highly useful idiom, and generally-speaking, dmd is supposed to come
in line with TDPL rather than TDPL being in error. So, I'd definitely argue
that the access level should have nothing to do with the overridability of a
function, regardless of what was originally intended for D. We have final if we
want to make functions non-overridable. There's no need to overload access
level to make it do the same thing. I'd say that dmd and the spec should come
in line with TDPL in this case.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Aug 11 2010
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=4542


Andrei Alexandrescu <andrei metalanguage.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|NEW                         |ASSIGNED
                 CC|                            |andrei metalanguage.com
         AssignedTo|nobody puremagic.com        |andrei metalanguage.com


-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Jan 09 2011
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=4542


Andrei Alexandrescu <andrei metalanguage.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
         AssignedTo|andrei metalanguage.com     |bugzilla digitalmars.com


--- Comment #3 from Andrei Alexandrescu <andrei metalanguage.com> 2011-01-16
15:21:22 PST ---
Reassigning to Walter - this is a compiler issue.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Jan 16 2011
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=4542


Brad Roberts <braddr puremagic.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
           Platform|x86_64                      |x86


--- Comment #4 from Brad Roberts <braddr puremagic.com> 2011-02-06 15:39:29 PST
---
Mass migration of bugs marked as x86-64 to just x86.  The platform run on isn't
what's relevant, it's if the app is a 32 or 64 bit app.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Feb 06 2011
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=4542



--- Comment #5 from Jonathan M Davis <jmdavisProg gmx.com> 2011-03-01 17:40:31
PST ---
I'm going to chime in on this to say that I definitely recant what I said
previously on this. I do _not_ think that TDPL is correct in this case. After
having discussed it on the newsgroup previously, it was shown that you could do
NVI with protected just fine and that using private for that doesn't actually
buy you anything. However, if we make private overridable, then all of a sudden
the default case for private is virtual and thus non-inlineable, which will
definitely have a negative effect on performance. It _is_ possible to mark a
private function as final to make it non-virtual and thus inlineable, but that
means that if you want efficient private functions, you're going to have to get
in the habit of specifically marking all of your private functions as final -
just so that NVI can be done with private instead of protected in spite of the
fact that protected does the job just fine.

So, making private overridable makes the default case inefficient for
essentially no gain.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Mar 01 2011
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=4542


Andrej Mitrovic <andrej.mitrovich gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |andrej.mitrovich gmail.com


--- Comment #6 from Andrej Mitrovic <andrej.mitrovich gmail.com> 2011-09-08
18:53:20 PDT ---
(In reply to comment #5)
 but that
 means that if you want efficient private functions, you're going to have to get
 in the habit of specifically marking all of your private functions as final
Can't the compiler determine this automatically? -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Sep 08 2011
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=4542



--- Comment #7 from Jonathan M Davis <jmdavisProg gmx.com> 2011-09-08 19:15:21
PDT ---
(In reply to comment 6)
 Can't the compiler determine this automatically?
No. In order to do that, it would have to know about every single class in the program that's derived from that class - directly or indirectly. That can't be known before linking. And by then, the function is already virtual or not. The compiler can only make a class' member function non-virtual when it can _know_ that no virtual calls to that function will ever be made. And that's can't generally be known. final guarantees it. I'm not sure that there's any other case when the compiler can determine it. Best case, there are some situations where it can know that a particular call doesn't need to be virtual. e.g. In (new A()).func() func could be done non-virtually by the compiler if it chose to optimize the code that way, because it can _know_ the exact type of A and avoid the virtual call (I don't know if it does). However, you can't even do that sort of optimization very often if you don't do flow analysis, which dmd typically avoids. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Sep 08 2011
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=4542



--- Comment #8 from Andrej Mitrovic <andrej.mitrovich gmail.com> 2011-09-08
19:21:38 PDT ---
Well in any case, unless you interleave your private and public functions, you
can use a shortcut:

class Foo
{
    final:

    private void x() {}
    private void y() {}
}

Has to be done for every class though, so maybe that's too much work..

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Sep 08 2011
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=4542



--- Comment #9 from Jonathan M Davis <jmdavisProg gmx.com> 2011-09-08 19:31:08
PDT ---
Oh. It's totally feasible to mark all of a class' private member functions
final. Personally, I always use

private:

already, so adding

final:

on top of that isn't exactly hard. However, the problem is that then everyone
has to remember to do it. The default that people are generally going to want
for private functions is that they be non-virtual, so if the default is
virtual, that's a problem. Most private functions will then be virtual when
they really should have been non-virtual, and it will cost performance -
especially since you can't normally inline virtual functions.

NVI is fairly rare, I think, and that's the only reason that I'm aware of that
anyone would want to have a virtual private function. And since NVI can be done
with protected anyway, I really don't think that it's much of a loss to make
private always non-virtual as it is now. But TDPL says that private is virtual,
so either we need to decide to update TDPL's errata accordingly or to change
private to virtual to bring dmd in line with TDPL. Given the relatively low
benefit for a fairly high cost, I really hope that we end up going against TDPL
in this case. But it's obviously not my decision.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Sep 08 2011
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=4542



--- Comment #10 from Andrej Mitrovic <andrej.mitrovich gmail.com> 2011-09-08
20:22:35 PDT ---
Isn't this what NVI is all about?

import std.stdio;

interface Foo
{
    final void callable()
    {
        impl1();
        impl2();
    }

    void impl1();
    void impl2();
}

class Bar : Foo
{
     disable void impl1() { writeln("impl1"); }
     disable void impl2() { writeln("impl2"); }
}

void main()
{
    Foo foo = new Bar();
    foo.callable();
}

You can't directly call impl1() from within Bar, however you can call it via
the Foo interface. I think this is pretty much it, no?

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Sep 08 2011
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=4542



--- Comment #11 from Andrej Mitrovic <andrej.mitrovich gmail.com> 2011-09-08
20:24:44 PDT ---
(In reply to comment #10)
 however you can call it via
 the Foo interface. 
* You can also call callable() via Bar, so this works fine: Bar bar = new Bar(); bar.callable(); -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Sep 08 2011
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=4542



--- Comment #12 from Jonathan M Davis <jmdavisProg gmx.com> 2011-09-08 20:40:36
PDT ---
The idea is to have a public, non-virtual function which does something before
and/or after calling a private virtual function. That way, you can guarantee
that certain things happen before or after the call to the private function.
However IIRC, you can't actually make the private function non-callable. I
forget what the details are. It was discussed in the newsgroup a while back,
and Steven Schveighoffer was able to show that using private instead of public
doesn't really buy you much in the way of protection - and the idiom works just
fine when you use protected instead of private. It just means that there's no
protection whatsoever against a derived class calling the virtual function
directly. So, it's by convention at that point, but since you could get around
it with private anyway (I don't remember exactly how at the moment,
unfortunately), it private doesn't actually give you that guarantee anyway.

So, NVI can be done with protected just fine. It might not be quite as nice
that way, but it works. On the other hand, making private virtual will result
in a systematic degredation in the performance of D programs in general. It can
be overcome with final, but it mean that the default is worse performance.

So, I really don't see much benefit in making private virtual. Protected will
give you the main benefits of NVI just fine, whereas making private virtual
gives you the slight benefit of increased cleanness in the use of NVI in some
programs at the cost of performance in pretty much all programs.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Sep 08 2011
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=4542



--- Comment #13 from Andrej Mitrovic <andrej.mitrovich gmail.com> 2012-10-19
15:35:23 PDT ---
What should have been done from day #1 is to implement the 'virtual' keyword
and make methods non-virtual by default. It goes hand in hand with 'override'.
I really don't know why virtual is on by default, maybe someone thought
polymorphism would be used a lot in D but it turns out templates are much
cooler to work with these days rather than OOP, just look at Phobos for
example. (ok the last part is highly subjective :) )

(In reply to comment #12)
 at the cost of performance in pretty much all programs.
I wonder what would happen to performance if we suddenly switched behavior and made methods non-virtual by default (and require a 'virtual' keyword). Of course you'd have to fix your code and add 'virtual' to base methods, but this is practically an error-free refactoring since 'override' was already required. You would get CT errors rather than weird runtime behavior (like C++03). -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Oct 19 2012
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=4542


Jacob Carlborg <doob me.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |doob me.com


--- Comment #14 from Jacob Carlborg <doob me.com> 2012-10-20 06:35:47 PDT ---
(In reply to comment #13)

 Of course you'd have to fix your code and add 'virtual' to base methods, but
 this is practically an error-free refactoring since 'override' was already
 required. You would get CT errors rather than weird runtime behavior (like
 C++03).
It's really not. You can have a class that someone else inherits from, which you don't control. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Oct 20 2012
prev sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=4542


Andrej Mitrovic <andrej.mitrovich gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |sergei.nosov gmail.com


--- Comment #15 from Andrej Mitrovic <andrej.mitrovich gmail.com> 2013-06-20
06:30:49 PDT ---
*** Issue 10422 has been marked as a duplicate of this issue. ***

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Jun 20 2013