ericm@ims.UUCP (Eric Martinson) (11/21/90)
The following is a posted list of bugs found in Turbo C++. The list was uploaded to Borland via BIX (Byte Info eXchange). Borland responded to the list and reposted it on BIX. I obtained acceptance from Borland to repost it on News, include BI's comments. There isn't a great deal of information here. The most interesting point is that BI actually responded. There may be a bug discussed here that you have run into. The list was created, as shown below, by Marshal P. Cline, Ph.D. I don't want to take credit, all I did was send it to BI and get a response. Product: Borland Turbo-C++ 1.0 Author: Marshall P. Cline, Ph.D. ECE department Clarkson University Potsdam, NY 13676 Voice: 315-268-3868 Secretary: 315-268-6511 FAX: 315-268-7600 ARPA: cline@sun.soe.clarkson.edu -or- bh0w@clutx.clarkson.edu Bitnet: BH0W@CLUTX UUnet: uunet!clutx.clarkson.edu!bh0w Copyright: The Author releases this to the Public Domain Date: June 11, 1990 Revised: August 9, 1990 Revised: August 14, 1990 Copyright: This file, along with code fragments it contains, is public domain. That means no one (including myself) can restrict its use/distribution. In particular, you can't copyright it. No one can. Not even me. Contributions: If you have a TC++ bug to report, please email to the above addresses. But please try to find/send a work-around to the problem/bug. Also please explicitly state that your comments/code are public domain. ========================== borland/long.messages #27, from lbenner, 9387 chars, Fri Nov 9 18:58:20 1990 -------------------------- TITLE: Borland's Comments to Emartinson's Bug List Borland has added comments to the items listed in this file originally posted as #25 from emartinson. Lori Borland ----------------------------------------------------------------- [1] Several classlib\include\*.h have #include "foo.h" rather than <foo.h>. This will only cause a problem if you have an indentically named header file in your current working directory (#include "file.h" starts in current dir, then searches the "standard places"; <file.h> only searches standard places). Note that TC++ by default doesn't find the classlib header files; if you want it to, add the line to turboc.cfg: -IC:\TC\CLASSLIB\INCLUDE BI comments: Not a "BUG". Discussion of how include files work is correct by ANSI C. The coding of the examples reflects the coding style of the ple who generated them. This could be changed, but not a high ority issue. ================================================================= [2] Some include files have #ifndef __FILENAME_H, others have #ifndef _FILENAME_H. (inconsistent #leading underscores). See for example sortable.h vs set.h, etc. BI comments: True this is inconsistent, but causes NO problems. Perhaps it could be changed, but not high priority. ================================================================= [3] TCCNVT.EXE (the configuration file converter) isn't in the distribution. BI comments: TRUE. Still not available. ================================================================= [4] `make -n' looks for and reads `builtins.mak' ONLY if it's in the current dir. Naturally this is a bug, since make can't give an accurate picture of what a real `make' would do without the macros and implicit rules in `builtins.mak'. BI comments: TRUE. Fixed as of version 1.01. ================================================================= [5] <iostream.h> always includes <mem.h> which slows compilation some. In fact <iostream.h> doesn't need `NULL', and only needs memcpy() if _BIG_INLINE_ is defined, which will probably be rare. Therefore the line #include <mem.h> // to get memcpy and NULL can be replaced with: #ifdef _BIG_INLINE_ #include <mem.h> // to get memcpy #endif Since nearly everything includes <iostream.h>, and since relatively few things will want _BIG_INLINE_, this should be a winner. Note however that some code might assume <iostream.h> pulls in <mem.h>. BI comments: iostream.h is big and slow to compile. Other speedups are to remove all the comments from the file. This speeds up 286 machines. ================================================================= [6] <iomanip.h> needs <iostream.h> but doesn't get it. Add this to <iomanip.h>: #ifndef __IOSTREAM_H #include <iostream.h> #endif BI comments: TRUE. Not in AT&T spec. Not changed in current version. Probably should be. ================================================================= [7] There is no <new.h>. I constructed the following work-alike. It should go into your standard include directory (where <iostream.h> is, for example): // new.h // Author: Dr. Marshall Cline/ECE Dept/Clarkson // Univ/Potsdam,NY 13676 // Email: cline@sun.soe.clarkson.edu // Phone: Voice: 315-268-6511; Fax: 315-268-7600 // Copyright: The Author releases this to the Public Domain, 9-July-90. // Date: 9-July-1990 // Please include these acknowledgements when distributing this file #ifndef __NEW_H #define __NEW_H #ifndef _SIZE_T #define _SIZE_T typedef unsigned size_t; #endif void* operator new(size_t size, void* ptr); // _new_handler is a ptr to a parameterless function returning void extern void (*_new_handler)(); void (*set_new_handler(void (*replacement_handler)()))(); #endif __NEW_H BI comments: NEW.H is NOT defined by AT&T 2.0 C++ specs. The contents were in C++ specs 1.2 and is making a comeback in the upcoming ANSI C++ spec. We do have the underlying code, ie _new_handler support, but it is not documented. It would be nicer to use a typedef for the void function pointer, for the above work-around. ================================================================= [8] Bug in istream: an extremely common C++ main input loop is something like: while (cin >> chunk) chunk.do_something(); This works under the condition that istream::operator void* returns 0 (false) after the input stream reads past EOF (or encounters an error). TC++'s iostream.h is consistent with its documentation [programmer's guide p.183] in stating that this operator returns 0 only when istream::fail() is true (when failbit, badbit or hardfail are set), but unfortunately `fail' doesn't get set after you've read past EOF. The correct operation is to return 0 (false) on the read after you've run into EOF as well [Lippman p.384], which CAN BE accomplished by the _bad bit being set wnen seeking past end-of-file [Lippman p.402]. This can be fixed by changing "ios::operator void*()" in <iostream.h> as follows: inline _Cdecl ios::operator void* () { return (state & (eofbit|failbit|badbit|hardfail)) ? 0 : this; } NB: the `official' (if there is such a thing in these ANSI-C++ pre-days) vior of istream::operator>> isn't clear. I've behadiscussed matter with Borland, and with a collegue who is in bothcharge of certain libraries de AT&T, and no one is yet sure insiwhat is really ``supposed'' appen. above patch (checking the The eofbit) appears to work correctly, it may hat a more be tcomprehensive solution is eventually in order. ny event, mostpeople's code doesn't run around checking individual bits de insian ios, he above is probably `safe'. BI comments: Although this code happens to work in AT&T's CFRONT implementation of C++, there is no documentation stating how this code should operate. To test for EOF we recommend breaking the statement down ie ifstream joe; while (!joe.eof()) { joe >> ch; } This should be up for debate by the C++ developers. ================================================================= [9] There is an error in TCC that isn't in TC (they generate different code). [Borland is as surprised that they'd behave differently as I was; I'd imagine the internals are identical, and this assumption has be confirmed by Borland]. When a virtual fn is called from a non-virtual inline member, the virtualness of the call vanishes. Compile the following with `tcc -vi': #include <iostream.h> class B { public: virtual void virt(); void nonvirt() { cout << "B::nonvirt() calling "; virt(); } }; class D : public B { public: void virt(); }; main() { D d; (&d)->nonvirt(); // B::nonvirt() should call D::virt() d.nonvirt(); // ditto } void B::virt() { cout << "B::virt()\n"; } void D::virt() { cout << "D::virt()\n"; } Unfortunately both of these call B::nonvirt(). Ie:Both d.nonvirt() & (&d)->nonvirt() translate to "call near ptr @B@virt$qv". Obviously these should be virtual function calls. This is a serious error, as calling a virtual from a non-virtual is fairly common. Note: if B::virt() is a pure virtual (another legal operation, but perhaps not as common), the call to "@B@virt$qv" generates a linker error (there is no B::virt()!). If B::nonvirt() is a regular (non-inline) function (either by moving it out of the class, or by `-v' or `-vi-'), TCC generates the correct code. Strangely enough, TC appears to *always* generate the correct code. BI comments: TRUE, and fixed as of version 1.01. ================================================================= [10] The 1.2 streams package (called <stream.h>) is nice, however AT&T treats the inclusion of <stream.h> as an alias to <iostream.h>. Therefore you should rename it from <stream.h> to <oldstream.h>, and let <stream.h> simply be: #include <iostream.h> It is notable that a number of posters on comp.lang.c++ have been confused by including <stream.h> thinking they were getting <iostream.h>... BI comments: TRUE, but this is a 2.0 implementation and have included oldstreams as an added feature. ================================================================= [11] <generic.h>: Instead of using the usual "name2" style macros, Borland invented their own set of macros for concatenating pieces of names. Any code using the Stroustrup-style macros (eg. code compatable with g++, CC, or Zortech C++) will break. A work-around is to stick the following on the bottom of your <generic.h>: #define name2 _Paste2 #define name3 _Paste3 #define name4 _Paste4 This bug and its work-around is thanks to: Pete Humphrey / pete@edsr.eds.com / 505-345-1863 EDS Research / 5951 Jefferson Street NE / Albuquerque, NM 87109-3432 BI comments: TRUE. As to why . . . copyright issues. ================================================================= [12] TC++ signals a compiler error when the LAST default parameter for a constructor is initialized by a constructor for some other class. A workaround is to add an extra dummy default parameter of some predefined type like int. Ex: if A and B are classes, then: This will be an error: A::A(B b = B(...)); But this is ok: A::A(B b = B(...), int dummy=0); Thanks to:Zerksis Umrigar/umrigar@bingvaxu.cc.binghamton.edu/SUNY Binghamton NY BI comments: Could not reproduce. ================================================================= [13] When an object is created as a side effect of an expression, and the expression is short-circuited, the destructor for the object seems to get called anyway: class X { public: X create_copy_of_X() { return *this; } //Return *BY VALUE* int whatever(); //... }; main() { X x; //... if (0 && x.create_copy_of_X().whatever()) stmt; //... } At the close of main(), TC++ calls a destructor on the copy of `x' even though the copy was never properly constructed. This only happens when inline functns are NOT inlined. Thanks to: Jamshid Afshar/jamshid@ccwf.cc.utexas.edu BI comments: True. Fixed in v1.01 ================================================================= -- This is a test