This is the mail archive of the
libstdc++@gcc.gnu.org
mailing list for the libstdc++ project.
libstdc++ debug mode ideas 4
- From: Benjamin Kosnik <bkoz at redhat dot com>
- To: libstdc++ at gcc dot gnu dot org
- Date: Wed, 6 Aug 2003 14:28:43 -0500
- Subject: libstdc++ debug mode ideas 4
Begin forwarded message:
Date: 31 Jul 2003 00:56:59 +0200
From: Gabriel Dos Reis <gdr@integrable-solutions.net>
To: Doug Gregor <dgregor@apple.com>
Cc: Benjamin Kosnik <bkoz@redhat.com>
Subject: Re: [libstdc++ PATCH] libstdc++ debug mode (second try)
Doug Gregor <dgregor@apple.com> writes:
| On Wednesday, July 30, 2003, at 10:21AM, Gabriel Dos Reis wrote:
| > | The fact is that, in the absence of a perfect template aliasing
| > model,
| > | compiling part of a program in release mode and part of it in debug
| > | mode is an ODR violation. If you agree with this, then I only see two
| > | options:
| > | (1) Find that perfect template aliasing model, or
| > | (2) Manage the ODR violation so that it can't cause trouble.
| >
| > I do not think it is a template aliasing model and that finding that
| > template alising model makes the problem (if there is one) go away.
| >
| > The template-name is primarily there to provide name commonality.
| > What is being violated is not the declaration of the template-name,
| > but the ODR for the entity being named by the template.
|
| I think a perfect template aliasing model would make the problem go
| away because we could do this:
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.
| 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.
| 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.
| 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.
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).
| > | I see that include <debug/vector> makes std::vector the debug
| > | version even if we are compiling in release mode.
| >
| > I must confess my sympathy for this approach. It is the most simple
| > semantics I would expect.
|
| 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.
| I personally think the former is more important,
| because I suspect that users would prefer to debug programs that
| crash, say, after an iterator dereference by turning debugging on only
| for a few suspect container instances and compiling only a little
| code. The <debug/vector> model will turn on debugging for a lot more
| container instances, and I wonder how much use it would get vs. just
| turning on _GLIBCXX_DEBUG.
|
| > | This differs from my
| > | original patch, where <debug/vector> declares a debugging vector as
| > | __gnu_debug::vector (regardless of whether or not we're in debug
| > | mode). The benefit of your approach is that users can change one
| > | #include to get debugging for a certain container type; the
| > | disadvantage is that you can't change it for a single container
| > | instance (since there is no separate __gnu_debug::vector to use). The
| > | other issue is that
| > |
| > | #include <vector>
| > | #include <debug/vector>
| > |
| > | will fail to compile in release mode, because std::vector will be
| > | defined twice and __std::vector will not be defined at all. With this
| > | methodology, the best we could do would be to emit a warning that the
| > | debug-mode vector will _not_ be used, and then skip the debug
| > | version. I wonder how many users would leave a stray <debug/vector>
| > | somewhere in their program and wonder why things are running slowly.
| >
| > 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