This is GCC Bugzilla
This is GCC Bugzilla Version 2.20+
View Bug Activity | Format For Printing | Clone This Bug
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()
2.95.3 ICEd on this. I don't know if I can consider this a regression. Confirmed.
(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
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.
(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.
(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.
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. >
(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!
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/
(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!
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/
(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!
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/
(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
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; };
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.
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
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/
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/