Bug List: (This bug is not in your last search results)   Show last search results      Search page      Enter new bug
Bug#: 33878
Product:  
Component:  
Status: RESOLVED
Resolution: INVALID
Assigned To: Not yet assigned to anyone <unassigned@gcc.gnu.org>
Host:
Reported against  
Priority:  
Severity:  
Target Milestone:  
 
 
Target:
Reporter: Björn A. Herwig <herwig@gdsys.de>
Add CC:
CC:
Remove selected CCs
Build:
URL:
Summary:
Keywords:
Known to work:
Known to fail:

Attachment Description Type Created Size Actions
Create a New Attachment (proposed patch, testcase, etc.) View All

Bug 33878 depends on: Show dependency tree
Show dependency graph
Bug 33878 blocks:

Additional Comments:






View Bug Activity   |   Format For Printing   |   Clone This Bug


Description:   Last confirmed: 2007-11-19 05:36 Opened: 2007-10-24 09:03
The following stripped down code shows pure virtual method definitions for both
a normal base class and a templated base class. To my surprise, the templated
class' body is not generated, leading to a "pure virtual method called"
termination in my actual threaded code. Leaving out the "= 0" in the template
generates the body TBase<int>::pvMethod() but is inacceptable due to not
forcing implementation by the derived classes.

Regards,

Björn A. Herwig
Guntermann & Drunck GmbH Systementwicklung 
Dortmunder Str. 4a 
D-57234 Wilnsdorf - Germany

---

bah@sw5bah:~/Programming$ LC_ALL=C g++ -v -save-temps -O0 -Wall -o test
test.cpp
Using built-in specs.
Target: i486-linux-gnu
Configured with: ../src/configure -v
--enable-languages=c,c++,fortran,objc,obj-c++,treelang --prefix=/usr
--enable-shared --with-system-zlib --libexecdir=/usr/lib
--without-included-gettext --enable-threads=posix --enable-nls
--with-gxx-include-dir=/usr/include/c++/4.2 --program-suffix=-4.2
--enable-clocale=gnu --enable-libstdcxx-debug --enable-mpfr
--enable-targets=all --enable-checking=release --build=i486-linux-gnu
--host=i486-linux-gnu --target=i486-linux-gnu
Thread model: posix
gcc version 4.2.3 20071014 (prerelease) (Debian 4.2.2-3)
 /usr/lib/gcc/i486-linux-gnu/4.2.3/cc1plus -E -quiet -v -D_GNU_SOURCE test.cpp
-mtune=generic -Wall -O0 -fpch-preprocess -o test.ii
ignoring nonexistent directory "/usr/local/include/i486-linux-gnu"
ignoring nonexistent directory
"/usr/lib/gcc/i486-linux-gnu/4.2.3/../../../../i486-linux-gnu/include"
ignoring nonexistent directory "/usr/include/i486-linux-gnu"
#include "..." search starts here:
#include <...> search starts here:
 /usr/include/c++/4.2
 /usr/include/c++/4.2/i486-linux-gnu
 /usr/include/c++/4.2/backward
 /usr/local/include
 /usr/lib/gcc/i486-linux-gnu/4.2.3/include
 /usr/include
End of search list.
 /usr/lib/gcc/i486-linux-gnu/4.2.3/cc1plus -fpreprocessed test.ii -quiet
-dumpbase test.cpp -mtune=generic -auxbase test -O0 -Wall -version -o test.s
GNU C++ version 4.2.3 20071014 (prerelease) (Debian 4.2.2-3) (i486-linux-gnu)
        compiled by GNU C version 4.2.3 20071014 (prerelease) (Debian 4.2.2-3).
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
Compiler executable checksum: c541ec01cc4b43709b709460c241205f
 as -V -Qy -o test.o test.s
GNU assembler version 2.18 (i486-linux-gnu) using BFD version (GNU Binutils for
Debian) 2.18
 /usr/lib/gcc/i486-linux-gnu/4.2.3/collect2 --eh-frame-hdr -m elf_i386
--hash-style=both -dynamic-linker /lib/ld-linux.so.2 -o test
/usr/lib/gcc/i486-linux-gnu/4.2.3/../../../../lib/crt1.o
/usr/lib/gcc/i486-linux-gnu/4.2.3/../../../../lib/crti.o
/usr/lib/gcc/i486-linux-gnu/4.2.3/crtbegin.o
-L/usr/lib/gcc/i486-linux-gnu/4.2.3 -L/usr/lib/gcc/i486-linux-gnu/4.2.3
-L/usr/lib/gcc/i486-linux-gnu/4.2.3/../../../../lib -L/lib/../lib
-L/usr/lib/../lib -L/usr/lib/gcc/i486-linux-gnu/4.2.3/../../.. test.o -lstdc++
-lm -lgcc_s -lgcc -lc -lgcc_s -lgcc /usr/lib/gcc/i486-linux-gnu/4.2.3/crtend.o
/usr/lib/gcc/i486-linux-gnu/4.2.3/../../../../lib/crtn.o
bah@sw5bah:~/Programming$ cat test.cpp
class Base
{
public:
        virtual void pvMethod() = 0;
};

void Base::pvMethod()
{
}

class Derived :
        public Base
{
public:
        void pvMethod() { }
};

template<class T>
class TBase
{
public:
        virtual void pvMethod() = 0;
};

template<class T>
void TBase<T>::pvMethod()
{
}

class TDerived :
        public TBase<int>
{
public:
        void pvMethod() { }
};

int main(int argc, char** argv)
{
        Derived d;
        TDerived td;
        return 0;
}
bah@sw5bah:~/Programming$ cat test.ii
# 1 "test.cpp"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "test.cpp"
class Base
{
public:
 virtual void pvMethod() = 0;
};

void Base::pvMethod()
{
}

class Derived :
 public Base
{
public:
 void pvMethod() { }
};

template<class T>
class TBase
{
public:
 virtual void pvMethod() = 0;
};

template<class T>
void TBase<T>::pvMethod()
{
}

class TDerived :
 public TBase<int>
{
public:
 void pvMethod() { }
};

int main(int argc, char** argv)
{
 Derived d;
 TDerived td;
 return 0;
}
bah@sw5bah:~/Programming$ objdump -t test | c++filt | grep ::pvMethod
08048506  w    F .text  00000005              TDerived::pvMethod()
080484c4 g     F .text  00000005              Base::pvMethod()
08048500  w    F .text  00000005              Derived::pvMethod()

------- Comment #1 From Andrew Pinski 2007-11-19 05:36 -------
2.95.3 ICEd on this.  I don't know if I can consider this a regression.

Confirmed.

------- Comment #2 From Björn A. Herwig 2007-11-20 07:54 -------
(In reply to comment #1)
> 2.95.3 ICEd on this.  I don't know if I can consider this a regression.
> 
> Confirmed.
> 

Shouldn't the keyword say "wrong-code" rather than "accepts-invalid"? Defining
a pure virtual method is valid code, shouldn't that apply to templates as well?

Regards,

Björn A. Herwig
Guntermann & Drunck GmbH Systementwicklung 
Dortmunder Str. 4a 
D-57234 Wilnsdorf - Germany

------- Comment #3 From yuri 2008-03-30 22:29 -------
I believe that the main problem here is that GCC allows defining pure virtual
functions.  The compiler should report an error when these two functions are
defined:

//------------------------------------
void Base::pvMethod()
{
}

template<class T>
void TBase<T>::pvMethod()
{
}
//------------------------------------

I added bug #35772 to track this.

------- Comment #4 From Wolfgang Bangerth 2008-03-31 19:51 -------
(In reply to comment #3)
> I believe that the main problem here is that GCC allows defining pure virtual
> functions.

No, that's perfectly legal.
W.

------- Comment #5 From Wolfgang Bangerth 2008-03-31 19:54 -------
(In reply to comment #0)
> The following stripped down code shows pure virtual method definitions for both
> a normal base class and a templated base class. To my surprise, the templated
> class' body is not generated,

Your example code contains neither a call to TBase<int>::pvMethod (which would
trigger an implicit instantiation of this function from its template) nor an
explicit instantiation of either the entire class or of this function.
Consequently, the compiler doesn't instantiate your template.

If you intend to call TBase<int>::pvMethod from somewhere where the definition
of this template is not visible, you need to add an explicit instantiation of
this function for 'int' as the template arg.

W.

------- Comment #6 From yuri 2008-03-31 20:01 -------
Yes, it is legal, sorry confusion.
Yuri

(In reply to comment #4)
> (In reply to comment #3)
> > I believe that the main problem here is that GCC allows defining pure virtual
> > functions.
> 
> No, that's perfectly legal.
> W.
> 

------- Comment #7 From Björn A. Herwig 2008-04-01 07:58 -------
(In reply to comment #5)
> (In reply to comment #0)
> > The following stripped down code shows pure virtual method definitions for both
> > a normal base class and a templated base class. To my surprise, the templated
> > class' body is not generated,
> 
> Your example code contains neither a call to TBase<int>::pvMethod (which would
> trigger an implicit instantiation of this function from its template) nor an
> explicit instantiation of either the entire class or of this function.
> Consequently, the compiler doesn't instantiate your template.
> 
> If you intend to call TBase<int>::pvMethod from somewhere where the definition
> of this template is not visible, you need to add an explicit instantiation of
> this function for 'int' as the template arg.
> 
> W.
> 

Wolfgang,

thanks for the clarification. I should have realized it myself, though. I
solved the problem in another way, but out of pure curiosity: How can I
implicitly instatiate this function when it's ought to be called only as a
fallback, in case something went wrong (bad cast, call to half-destroyed object
from another thread, etc.). It needs to be a generic solution as the real
application is a library base template derived from a normal base class used in
a CRTP-manner. I think that is why an implicit instantiation is needed. Or am I
wrong here? Probably I am. :)

Thanks for your reply,
Björn!

------- Comment #8 From bangerth@math.tamu.edu 2008-04-01 12:52 -------
Subject: Re:  Pure virtual method body omitted from template


> thanks for the clarification. I should have realized it myself, though. I
> solved the problem in another way, but out of pure curiosity: How can I
> implicitly instatiate this function

That's a self-contradiction. The term "implicit instantiation" refers to 
something that the compiler does without any explicit user request, so 
there is no way to "implicitly instatiate this function yourself" :-)

You could explicitly instantiate this function, but...

> It needs to be a generic solution

The only possibility in this case would be to put the function into your 
header file. I believe that that should work in your case.

Best
 Wolfgang

-------------------------------------------------------------------------
Wolfgang Bangerth                email:            bangerth@math.tamu.edu
                                 www: http://www.math.tamu.edu/~bangerth/

------- Comment #9 From Björn A. Herwig 2008-04-01 14:38 -------
(In reply to comment #8)
> Subject: Re:  Pure virtual method body omitted from template
> 
> 
> > thanks for the clarification. I should have realized it myself, though. I
> > solved the problem in another way, but out of pure curiosity: How can I
> > implicitly instatiate this function
> 
> That's a self-contradiction. The term "implicit instantiation" refers to 
> something that the compiler does without any explicit user request, so 
> there is no way to "implicitly instatiate this function yourself" :-)

Great, I already had the feeling my brain dead-locked when thinking about a
solution. :)

> You could explicitly instantiate this function, but...
> 
> > It needs to be a generic solution
> 
> The only possibility in this case would be to put the function into your 
> header file. I believe that that should work in your case.

Okay, you mean in the header file where I derive a class from TBase. There I'll
write something like:

class MyClass : public TBase<MyClass>
{
// My class goes here, espacially overiding pvMethod()...
};

void TBase<MyClass>::pvMethod()
{
// Not so generic dummy function
// Copy and paste error handling here ;)
}

Not exactly what I wanted...

Or did you mean that the function definition is in the TBase header file? If
so: It is.

Thanks for your kind help!

Regards,
Björn!

------- Comment #10 From bangerth@math.tamu.edu 2008-04-01 14:44 -------
Subject: Re:  Pure virtual method body omitted from template


> Or did you mean that the function definition is in the TBase header file? If
> so: It is.

Yes. Since the class declaration must be visible from the place where you 
call this function, and since then also the function's definition 
(=implementation) is visible, the template should be instantiated at the 
place where you call the member function. Is this not the case?

Best
 W.

-------------------------------------------------------------------------
Wolfgang Bangerth                email:            bangerth@math.tamu.edu
                                 www: http://www.math.tamu.edu/~bangerth/

------- Comment #11 From Björn A. Herwig 2008-04-02 07:17 -------
(In reply to comment #10)
> Yes. Since the class declaration must be visible from the place where you 
> call this function, and since then also the function's definition 
> (=implementation) is visible, the template should be instantiated at the 
> place where you call the member function. Is this not the case?

No, it is not. And that's because this pure virtual method never gets called
explicitly.

I think the compiler is taking an illegal shortcut in assuming a pure virtual
template method never gets called. When deriving a class from the template, the
vtable is built and it surely has a slot for pvMethod() because it's a virtual
method. So the template is (fully?) instantiated at the point, where I derive
from it. See the following example:

template<class T>
class TBase
{
public:
    virtual void pvMethod() = 0;
    virtual void vMethod();
};

template<class T>
void TBase<T>::pvMethod()
{
}

template<class T>
void TBase<T>::vMethod()
{
}

class TDerived :
    public TBase<TDerived>
{
public:
    void pvMethod();
};

void TDerived::pvMethod()
{
}

int main(int argc, char** argv)
{
    return 0;
}

There is no instance of TDerived whatsoever. But looking at the binary reveals
there is even a TBase<TDerived>::vMethod():

bah@sw5bah:~/Programming$ g++ -O0 -Wall test.cpp -o test && objdump -t test |
c++filt | grep '\(TBase\|TDerived\)'
08048474 g     F .text  00000005              TDerived::pvMethod()
08048570  w    O .rodata        0000000a              typeinfo name for
TDerived
0804857c  w    O .rodata        0000000c              typeinfo for TDerived
08048560  w    O .rodata        00000010              vtable for TDerived
08048590  w    O .rodata        00000012              typeinfo name for
TBase<TDerived>
08048494  w    F .text  00000005              TBase<TDerived>::vMethod()
08048588  w    O .rodata        00000008              typeinfo for
TBase<TDerived>

Now, in my opinion there must be a TBase<TDerived>::pvMethod(), because
(a) it is legal to specify a fallback implementation,
(b) the slot is surely there, and
(c) as we found out, there is no workaround for it.

Perhaps (c) is a false conclusion because you or I missed something, or (a) is
wrong concerning templates, but nevertheless it's annoying that there's no
clean solution for it. In my code I solved it by making the pure virtual method
virtual only. But now there has been at least one occasion where I forgot to
provide an implementation in a derived class...

If you think my reasoning is right, would you mind reopening the bug?

Best regards,
Björn!

------- Comment #12 From bangerth@math.tamu.edu 2008-04-02 13:31 -------
Subject: Re:  Pure virtual method body omitted from template


> No, it is not. And that's because this pure virtual method never gets called
> explicitly.

The point I meant to make but failed is: a pure virtual method can *only* 
*ever* be called explicitly. It can't be called through the vtable because 
there can be no objects of the type of this pure class, only of derived 
classes, and in the vtables of this derived class the slot for this 
virtual function is filled by another function (because the derived class, 
to be instantiated, must have overwritten the pure function).

So, yes, I'm not surprised that the object file you produce from your 
testcase doesn't contain pvMethod. But that's not a problem because in it 
nobody ever calls this function. What I want to see is a testcase in which 
this function *should* be there because it is, somehow, called or 
otherwise required.

Does this make sense?

W.

-------------------------------------------------------------------------
Wolfgang Bangerth                email:            bangerth@math.tamu.edu
                                 www: http://www.math.tamu.edu/~bangerth/

------- Comment #13 From Björn A. Herwig 2008-04-02 16:07 -------
(In reply to comment #12)
> The point I meant to make but failed is: a pure virtual method can *only* 
> *ever* be called explicitly. It can't be called through the vtable because 
> there can be no objects of the type of this pure class, only of derived 
> classes, and in the vtables of this derived class the slot for this 
> virtual function is filled by another function (because the derived class, 
> to be instantiated, must have overwritten the pure function).

You are absolutely right as long as there is no multithreading and no dangling
pointer. Sure. The thing is: If it's called, something bad has occured and I
want to catch it let's say for a backtrace at this point. Or I take it
deliberately into account and let it perform a no-op.

When searching for an example, I came across this link:

http://www.artima.com/forums/flat.jsp?forum=226&thread=196881#270009

This post is the exact description of the problem I initially ran into. Even to
proposed solution is part of what I've done to prevent the call.

> So, yes, I'm not surprised that the object file you produce from your 
> testcase doesn't contain pvMethod. But that's not a problem because in it 
> nobody ever calls this function. What I want to see is a testcase in which 
> this function *should* be there because it is, somehow, called or 
> otherwise required.

Okay, here it is, the faulty code: ;)

#include <iostream>
#include <pthread.h>

using namespace std;

template<class T>
class TBase
{
public:
    ~TBase();
    virtual void pvMethod() = 0;
};

template<class T>
TBase<T>::~TBase()
{
    sleep(2);
}

template<class T>
void TBase<T>::pvMethod()
{
    cerr << "Error" << endl;
}

class TDerived :
    public TBase<TDerived>
{
public:
    void pvMethod();
};

void TDerived::pvMethod()
{
    cout << "OK" << endl;
}

int startThread(pthread_t* handle, void* (*entry)(void*), void* arg)
{
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, true);

    int res = pthread_create(handle, &attr, entry, arg);

    pthread_attr_destroy(&attr);

    return res;
}

void* thread(void* arg)
{
    TDerived** typed_arg = (TDerived**)arg;
    TDerived* obj = *typed_arg;
    sleep(1);
    delete obj;
    *typed_arg = 0;
    return 0;
}

int main(int argc, char** argv)
{
    TDerived* obj = new TDerived;
    pthread_t thread_handle;
    startThread(&thread_handle, &thread, &obj);

    for (int i = 0; i < 5; ++i) {
        if (!obj) {
            break;
        }
        obj->pvMethod();
        sleep(1);
    }

    return 0;
}

bah@sw5bah:~/Programming$ g++ -O0 -Wall -lpthread test.cpp -o test && ./test
OK
OK
pure virtual method called
terminate called without an active exception
Abgebrochen

> Does this make sense?

The standard says "A pure virtual function need be defined only if explicitly
called with the qualified-id syntax (5.1)." in 10.4.2, so it's perfectly legal
to omit the pvMethod(). But there is a discrepancy between templates and normal
classes:

* For normal classes the method is generated, even if not used. GCC developers
decided to do more than the standard requires, and they must have reasons to do
so. Here you have a choice.

* For templates the method is not generated, and we've discussed why. It's due
to the template function emission rules: Not called, not emitted. But there is
also the shortcut rule when deriving: Cannot be called, not emitted. That is
what you explained. Thus here you have no choice.

Honestly, wouldn't you also like to have the choice?

Best,
Björn!

--------------------------------------------------------------------------
Dipl.-Ing. Björn A. Herwig
Guntermann & Drunck GmbH Systementwicklung 
Dortmunder Str. 4a 
D-57234 Wilnsdorf - Germany 
Tel: +49 (0) 27 39 / 89 01 - 100  Fax: +49 (0) 27 39 / 89 01 - 120 
E-Mail: mailto:sales@gdsys.de Web: www.gdsys.de
--------------------------------------------------------------------------
Geschäftsführer:
Udo Guntermann - Martin Drunck - Reiner Ruelmann - Klaus Tocke
HRB 2884, Amtsgericht Siegen
USt.-Id.-Nr. DE 126575222 - Steuer-Nr. 342 / 5835 / 1041

------- Comment #14 From yuri 2008-04-02 17:15 -------
Hi Björn

My question is slightly off topic but I am really interested in the purpose of
defining a template class where a template parameter is not used.  Why would
you need this?

Regards,
Yuri

template<class T>
class TBase
{
public:
    ~TBase();
    virtual void pvMethod() = 0;
};

------- Comment #15 From David Fang 2008-04-02 17:38 -------
Unused template parameters can be used when you want to intentionally subtype a
base type with different flavors that are incompatible with each other, using
compile-time checking to prevent accidental cross-contamination, for instance.  

------- Comment #16 From yuri 2008-04-02 17:58 -------
Thanks for the reply, David!  But now I have more questions than I had before
:-)

I'm not sure if this thread is the right place to go into details on this
topic.  If you know any other place to move this discussion, please let me
know.

What do you mean by "different flavors that are incompatible with each other"?
Could you tell a little more about "accidental cross-contamination"?

Giving a short example for each question would help a lot :-)

Thanks in advance,
Yuri

------- Comment #17 From bangerth@math.tamu.edu 2008-04-02 18:31 -------
Subject: Re:  Pure virtual method body omitted from template


On Wednesday 02 April 2008 12:15:53 yuriry at gmail dot com wrote:
> My question is slightly off topic but I am really interested in the purpose
> of defining a template class where a template parameter is not used.  Why
> would you need this?

The class in this example is a stripped-down version for the purpose of 
exposition. In practice, of course, most of the time one would use the 
template argument somehow.

W.

-------------------------------------------------------------------------
Wolfgang Bangerth                email:            bangerth@math.tamu.edu
                                 www: http://www.math.tamu.edu/~bangerth/

------- Comment #18 From bangerth@math.tamu.edu 2008-04-02 18:34 -------
Subject: Re:  Pure virtual method body omitted from template


> You are absolutely right as long as there is no multithreading and no
> dangling pointer. Sure. The thing is: If it's called, something bad has
> occured

I see your point, but in a case like this you are working outside what the 
standard says (it would call the program erroneous and its 
actions 'undefined'). The compiler's job being to translate what the standard 
describes, you can't rely on any such behavior.


> * For templates the method is not generated, and we've discussed why. It's
> due to the template function emission rules: Not called, not emitted. But
> there is also the shortcut rule when deriving: Cannot be called, not
> emitted. That is what you explained. Thus here you have no choice.

You can instantiate explicitly if you want this.

By the way, there is also a rule somewhere in the standard that says that only 
those templates must be instantiated that are actually needed; in other 
words, the compiler cannot instantiate more functions just because someone 
may want it to be there -- the functions must actually be *needed* for the 
program to work correctly.

W.

-------------------------------------------------------------------------
Wolfgang Bangerth                email:            bangerth@math.tamu.edu
                                 www: http://www.math.tamu.edu/~bangerth/

Bug List: (This bug is not in your last search results)   Show last search results      Search page      Enter new bug