This is the mail archive of the libstdc++@gcc.gnu.org mailing list for the libstdc++ project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

libstdc++ debug mode ideas 5



Begin forwarded message:

Date: Wed, 30 Jul 2003 17:31:59 -0700
From: Doug Gregor <dgregor@apple.com>
To: Gabriel Dos Reis <gdr@integrable-solutions.net>
Cc: Benjamin Kosnik <bkoz@redhat.com>
Subject: Re: [libstdc++ PATCH] libstdc++ debug mode (second try)



On Wednesday, July 30, 2003, at 3:56PM, Gabriel Dos Reis wrote:
> The point of my previous note is that the debug thingy does not just
> concern templates.  It concerns also normal classes/functions.  The
> thing you're after is a renaming mechanism, not a template aliasing
> mechanism. A template aliasing mechanism will just solve a tiny part of
> the whole thing.  It will miss the big picture.  I would not advise
> going that road.

Perhaps. But we need a renaming/aliasing mechanism with a finer 
granularity than namespace aliasing (even with these extensions) gives 
us. See below.

> | namespace std {
> |    namespace __release {
> |      template<typename T, typename Allocator = std::allocator<T> >
> |        class vector; // define release-mode vector
> |    }
> |    namespace __debug {
> |      template<typename T, typename Allocator = std::allocator<T> >
> |        class vector; // define debug-mode vector
> |    }
> | }
>
> Suppose we have a referencing construct, which in an imginary syntax
> does the following
>
>   namespace __gnu_release { }
>
>   namespace __gnu_debug { }
>
>   using std = _GLIBCXX_DEBUG ? __gnu_debug : __gnu_release;
>
> and after that you can reopen std:: as if it were an original
> namespace -- i.e. not a namespace-alias according to standard text.
> Then, I believe you'll be saved away from many troubles.

Won't work. There are certain components that _must_ be unchanged 
regardless of whether we are in debug more or release mode. std::cin is 
a major one: we can't have two std::cin's floating around, one from 
__gnu_release and one from __gnu_debug, because they won't work well 
together and we will break valid C++ programs. That one can be solved 
with a using declaration (since it's just an object), but what about 
std::basic_istream? It's in the type of std::cin, so it cannot change 
regardless of whether or not we're in debug mode.

There's a reason that the debug wrapper for basic_string does not 
replace std::basic_string when we're in debug mode. string's are 
returned from virtual functions in locales, so if one were to attach a 
facet compiled in release mode that expects to return a debug-mode 
string, and that facet were used in a translation unit compiled in 
release mode, it would die horribly. _Anything_ that affects the global 
state _must_ be shared for valid C++ programs to run properly; anything 
that needs to change for debug mode must be renamed/aliased so that 
there is no ODR violation come link time.

I don't care if we call it renaming or aliasing, but it has to be at a 
level of granularity where I can say "make __gnu_debug::vector look 
like std::vector". Again, link_name has this granularity.

> | Then, we add some directive that makes std::vector alias
> | std::__release::vector (when we're in release mode) or
> | std::__debug::vector (when we're in debug mode). A using declaration
> | very nearly gets us there, except that it breaks specialization:
> |
> | namespace std {
> | #ifdef _GLIBCXX_DEBUG
> |    using __debug::vector;
> | #else
> |    using __release::vector;
> | #endif
> | }
>
> Sometime ago (meaning ~1999), I thought about that sort of thing --
> for hiding some of our implementation details.  I think I abandoned the
> idea because of some name lookup issues.  I'll have to rethink about
> it to remember the details.

Just derive from something in namespace std.

> |   I think your template aliasing proposal would let us do this:
> |
> | namespace std {
> | #ifdef _GLIBCXX_DEBUG
> |    template<typename T, typename Allocator=std::allocator<T> >
> |      using vector = __debug::vector<T, Allocator>;
> | #else
> |    template<typename T, typename Allocator=std::allocator<T> >
> |      using vector = __release::vector<T, Allocator>;
> | #endif
> | }
>
> But it will not work well with Koenig lookup because the alias
> std::vector will resolve to the original templates and I'm worrying
> about  simple non-debuged functions like std::swap.

Just derive from something in namespace std.

> The scheme I outlined above does not have that problem -- it does not
> have mangling problem either (it acts like a "reference" to the
> original namespace).

FWIW, I have gone down this path before (that's why I listed it as #4 
previously), and I just don't see any way it will work.

> | Okay. We're trading off the ability to easily switch a particular
> | container instance to debug mode (e.g., by changing std::vector<...>
> | to __gnu_debug::vector<...> in the source) with the ability to change
> | all containers to debug versions (e.g., by including <debug/vector>
> | instead of <vector>).
>
> Yes, I acknowledge that.  However the kind of selection you're
> proposing comes with many complications, starting with inclusion order
> dependency.  I do not believe in that approach.  Rather, I think a
> per-translatin-unit basis selection is smoother and cleaner.

Looks like we have some miscommunication. My scheme does _not_ have any 
inclusion order dependencies, and it is quite simple:

#include<debug/vector> gives you a debugging vector named 
__gnu_debug::vector (doesn't matter whether you're in debug or release 
mode)

#include <vector> gives you a vector named std::vector (in debug mode, 
it's a debugging vector; in release mode, it's a release-mode vector)

I'm having a hard time seeing much benefit to turning all vectors into 
debugging vectors for a particular translation unit without just 
applying -D_GLIBCXX_DEBUG to turn all containers into debug containers. 
Can you imagine a use-case for this behavior? For reference, we needed 
precisely the functionality I proposed: there was some real strangeness 
with one of our std::map instances, and we could easily have caught it 
by changing it to __gnu_debug::map (this is what triggered the 
feature).

> | > I think we should simply take that, we should avoid inclusion order
> | > dependency and we should avoid second-guessing the programmer, by
> | > assuming that he is stupid.  I'm not sure how we can reconsile the
> | > above criteria but I will certainly object to changing the 
> semantics
> | > behind the back of the user.
> |
> | Any idea on how we can avoid the inclusion-order dependencies? If we
> | see both <debug/vector> and <vector>, so we always pick the debug
> | version? The release version? AFAICT, we're stuck with the first one
> | that's included (because it defines std::vector), and I think that's 
> a
> | bit confusing.
>
> I do see those as strong arguments against per-container-basis
> selection.  Rather, I prefer to rely on the compiler, which in -debug
> mode, will take care of doing the right magic.  It offers a unified
> view and is easily explained and implemented.
>
> -- Gaby

Two questions, then:
	1) What are the strong arguments for per-container-basis (as opposed 
to per-instance-basis) selection?
	2) What would this compiler magic look like?


	Doug


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]