Subject: considerations on the use of __STL_DEBUG Date: Fri, 27 Mar 1998 14:21:13 -0800 From: Mike Oliver To: abkgroup@nexus6.cs.ucla.edu As some of you know, there is a symbol __STL_DEBUG which, when defined at compile time, causes STL containers to perform various checks--so that it will tell you if, for example, you attempt to access an element of a vector beyond the vector's current size. Unfortunately, since this changes the underlying definitions of the containers, it has to be done "all at once" -- i.e. all the libraries to which you link must also have been compiled with __STL_DEBUG. There is an attempt at this in ~code/STL_DEBUG. Here the various project directories are mirrored using symbolic links (created with a script called dirscript present in ~code/STL_DEBUG). Even the makefile is a symlink; therefore you can't just run "make" in a directory; rather, you run makeDeb. (Oh, BTW, here's something that needs a global fix: Some projects now define their own debug symbols, e.g. HGDEBUG in HGraph. makeDeb and makeOpt omit these. Not sure of the best solution.) Placement is an exception to the symlink methodology; for some reason that I have not entirely determined, it doesn't seem to compile using symlinks. Therefore the source files must be copied to keep it up to date. (Update -- I just checked; the makefile for Placement appears to be just wrong. E.g. it only tries to compile the stuff in the MST8 subdir.) Even more unfortunately, there are certain things that compile without __STL_DEBUG that do not compile with it, and therefore some projects do not currently compile under __STL_DEBUG. However in some cases it is probably good methodology to use the versions compatible with __STL_DEBUG even if we didn't need it. A summary: 1) No automatic conversion from iterators to pointers: Without __STL_DEBUG, a vector iterator literally *is* a pointer, and therefore you can do stuff like const_cast it to remove constness from it, or do pointer arithmetic as in HGFEdge::getNumSrcs() (defined in HGraph/hgfixed.h). With __STL_DEBUG, an iterator is a class in its own right, and so that sort of thing won't work. The fix can be seen in the new hgfixed.h (in HGraph2.3.2, hereby released): Prepend &* to any iterator that you wish to use as a pointer. The iterator class defines operator*(), then you can take the address of the result. Unfortunately that isn't good enough if you want to cast away constness from an iterator, as in the following chunk of code from DB/dbCell.cxx : _nets.erase(const_cast (dupItr), const_cast (_nets.end())); The problem is you can get the pointers, but you can't convert them *back* to iterators, at least not easily. A possible solution is something like the following (from SAPlace): #ifdef __STL_DEBUG itSAPnet netsBegin() {return *(reinterpret_cast(&mEdges.begin()));} itSAPnet netsEnd() {return *(reinterpret_cast(&mEdges.end()));} #else itSAPnet netsBegin() {return itSAPnet(mEdges.begin());} itSAPnet netsEnd() {return itSAPnet(mEdges.end());} #endif but of course this is somewhat painful. (I do believe that it is safe, having glanced at the __STL_DEBUG versions of the iterators.) ************************************************************************* 2) No automatic conversion of iterators to "more constant" iterators. Here let me quote from a previous mail: Consider the following typedefs: typedef vector vec; typedef vector vec_const; typedef vec::iterator iter; typedef vec::const_iterator const_iter; typedef vec_const::iterator iter_const; typedef vec_const::const_iterator iter_const_iter; and the following declarations and assignments vec v; vec_const vc; iter i; const_iter ci; iter_const ic; const_iter_const cic; vc = v; ci = i; ic = i; cic = ci; cic = ic; cic = i; A priori, all of these seem to be well-defined and to preserve const protections. However not all will compile. *Without* __STL_DEBUG, you can't do vc=v; ic=i; It's sort of strange that ic=i is forbidden whereas cic=i is OK, but that's the way it is. Now *with* __STL_DEBUG it gets pickier; in addition to the above two, the following assignments are forbidden: cic = ci; cic = i; leaving as legal only ci = i; cic = ic; In some cases we can fix this problem by having different begin-end methods for the different sorts of iterators, e.g. nonconstant iterators, const iterators to nonconst objects, nonconst iterators to const objects, const iterators to const objects.