Topic: 27.4.2.7 ios_base not self-contained - bad programming practice?


Author: info@rayfract.com
Date: Wed, 6 Jul 2005 10:49:44 CST
Raw View

kanze@gabi-soft.fr wrote:
>
> Not really.  A conforming program can tell the difference: in
> particular, std::ios::fmtflags and std::wios::fmtflags must be
> the same type.  Given the way the two classes are separated, I
> presume that the intent and motivation for the separate,
> non-template base, was to require that there be just one type.
> IMHO, it seems like a lot of excess complication, for a very
> small benefit, but that's just me.  (You and Plauger seem to
> more or less agree, so at least I'm in good company.)

Langer/Kreft "Standard C++ IOStreams and Locales" chapter 2.1.1.1 "The
Stream Base Classes", paragraph "Responsibility for error handling" :

"Access to the stream state and the exception mask is via member
functions of class basic_ios<class charT, class Traits>. The reason for
this design is that ios_base is also used in the locale section of the
standard library, where it serves as an abstraction for passing
formatting information to the locale. If ios_base contained the error
indication, which includes throwing exceptions, these exceptions could
also be raised by functions of the locale. This effect is neither
intended nor acceptable".

HTH,

Siegfried Rohdewald.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: kanze@none.news.free.fr (James Kanze)
Date: Wed, 29 Jun 2005 19:53:16 GMT
Raw View
P.J. Plauger wrote:

 >>                                Of course, people who were
 >>involved in the standardization of clause 22 know that it is
 >>riddled with problems but this is not that obvious to users...

 > Exactly. And this is the point James Kanze tried to make, I
 > think.

Well, my point had nothing to do with clause 22 (but I'll go
along with Dietmar's statement about it).  It was said that the
design of iostreams sucks, and that's a statement I just can't
go along with.  There are a lot of good things in it.  There are
also some things that I find less good, but that I can
understand, given the context.

And then, there are things which I really can't explain.  The
separation of the orginal ios into ios_base and basic_ios, for
example.  Or why locale dependant code translation is not a
filtering streambuf.  Or for that matter, why the templates to
begin with.  What can I do with a basic_istream or a
basic_ostream instantiated on anything other than char or
wchar_t?  Or even why istream::get extracts, but
streambuf::sgetc doesn't.

But the fact that there are problems, and even serious problems,
in some of the details doesn't mean that the design sucks,
globally.  The separation of sink/source from formatting is very
good.  And the decision to use virtual functions to permit
customizing of the sink/source, but user defined operator
overloading for customization of formatting is exceptionally
good.

 > Overall, iostreams presents a decent interface to the world.
 > The mistake lay in having the C++ Standard expose too much of
 > the innards to the world.

And try to put too much additional functionality into a single
template: code translation could easily be a filtering
streambuf, and IMHO, we'd be better off with three separate
class suites: one for char text, one for wchar_t text, and one
for binary data.  (The first two would obviously build on the
third.)

Just my opinion, of course.

--=20
James Kanze                                 mailto: james.kanze@free.fr
Conseils en informatique orient=E9e objet/
                        Beratung in objektorientierter Datenverarbeitung
9 pl. Pierre S=E9mard, 78210 St.-Cyr-l'=C9cole, France +33 (0)1 30 23 00 =
34

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: dietmar_kuehl@yahoo.com (Dietmar Kuehl)
Date: Wed, 29 Jun 2005 19:52:58 GMT
Raw View
Before going into arguing let me separate a few issues here to
clarify what I'm arguing for:

- The standard does not require an implementation of 'ios_base'
  which cannot be used as a free standing object, although it
  allows to do so (it is undefined behavior to destroy an 'ios_base'
  object which is not properly initialized, i.e. an implementation
  is free to do choose what it does).
- The standard does not require an implementation to use the nifty
  counter idiom for initialization of the standard stream objects
  and it does not require double initialization of these objects.
  The only real requirement made by the nifty counter idiom is
  flushing the standard stream objects upon destruction of the last
  nifty counter.
- Whether it is a good idea to support a free standing 'ios_base'
  object is an entirely different question and I think we are in
  agreement that it is probably actually a bad idea given that an
  implementation is not required to support it.
- I'm not complaining in any form about any implementation. In
  particular, I see the importance of binary compatibility which may
  impose serious restriction on the design space for a solution.
  Also, I think an implementation working with many different
  compilers will reasonably choose to take advantage of freedom
  granted by the standard.

Actually, I think we are in violent agreement for most of these
aspects but not about all of them...

P.J. Plauger wrote:
> Okay, so you *can* initialize ios_base even though you don't have to,
> but what if your implementation *requires* that ios_base not be
> initialized in its constructor?

I take this as a statement that the standard does indeed not require
leaving 'ios_base' uninitialized but it was a choice of the
"implementation" which makes the requirement. Now, to support a
well-behaved 'ios_base' object (assuming for the moment that it is
desirable at all), the constructor of 'ios_base' could be overloaded
for the use within one of the standard stream objects (such
overloading is necessary for 'basic_ios' and 'basic_[io]stream'
anyway).

Since static memory is zero initialized before any constructors start,
a zero initialized 'ios_base' can easily be detected in the special
constructor e.g. by checking whether a pointer the locale object is
set up (the user cannot set this pointer to null. This is important
if you really double initialize an object: it is not only the
'ios_base' object but actually the entire stream which is double
initialized. Thus, 'basic_ios<...>::init()' will have been called
when an object is constructed again during static initialization.
Thus, the constructor will have to check whether the members are zero
and only set them up if they indeed are (or release possibly allocated
memoryand initialize the object again). The separation of the 'ios_base'
construction and the 'basic_ios<...>::init()' function does not help
with the double construction. In fact, I don't think it serves any
purpose at all and I guess it was intended to set up the stream state
flags which were put into 'basic_ios<...>' later anyway (however,
this is guesswork: any major changes to the IOStreams library predate
my participation in the C++ standardization).

> Our library uses the nifty counter
> trick for linkers that don't help out. We're pretty much obliged to
> leave ios_base uninitialized at construction.

I don't follow this logic! It is necessary to distinguish between a
"normal" initialization and initialization for one of the standard
stream objects. In a "normal" initialization you can simply initialize
all members. In the initialization for one of the standard stream
objects you know that the memory was originally zero initialized
(because this is what is done with all object with static storage
duration) and any deviation from this indicates that the object is
already initialized and should be left untouched. For example:

  namespace _Ios { enum _Index { _In, _Out, _Err, _Log } }
  std::ios_base::ios_base() { _Init(); }  // normal initialization
  std::ios_base::ios_base(_Ios::_Index _Obj) // standard object
  {
    if (!_Ploc) // this could never be set to 0 by a user!
    {
      _Init();
      if (_Ios::_Err == _Obj) _Fmtfl |= std::ios_base::unitbuf;
    }
  }

Since all members in the 'ios_base' object are PODs, it is even
guaranteed that they are left untouched unless they are explicity
initialized. The constructor of 'basic_ios' is actually quite trivial
but complicated a little bit because it is a template:

  template <typename _Ct, typename _Tr> struct _Aux;
  template <> struct _Aux<char, std::char_traits<char> > {
    static std::streambuf* _Fds[4];
  };
  std::streambuf* _Aux<char, std::char_traits<char> >::_Fds[4] = {};

  // the normal ctor: nothing special...
  template <typename _Ct, typename _Tr>
  std::basic_ios<_Ct, _Tr>::basic_ios(
    std::basic_streambuf<_Ct, _Tr>* _Sbuf) { this->init(_Sbuf); }

  template <typename _Ct, typename _Tr>
  std::basic_ios<_Ct, _Tr>::basic_ios(_Ios::_Index _Obj):
    std::ios_base(_Ios::_Index)
  {
    if (_Aux<_Ct, _Tr>::_Fds[_Obj] == 0)
    {
      _Aux<_Ct, _Tr>::_Fds[_Obj] = new _Stdbuf<_Ct, _Tr>(_Obj);
      this->rdbuf(_Aux<_Ct, _Tr>::_Fds[_Obj]);
    }
  }

While the locale pointer was used for the 'ios_base' object to indicate
that the object was constructed before, it is the stream buffer pointer
in the '_Fds' array for the 'basic_ios' object. This is important to
take care of the case where the user has made changes to the standard
stream object in a constructor of static object already. Again, the
construction of the 'basic_ios' object guarantees that the members are
untouched unless they are explicitly changed. The remaining classes
('basic_istream' and 'basic_ostream'), the missing specialization of
'_Aux' for 'wchar_t', and the standard stream objects are trivial and
don't require any trickery. Of course, the two stream classes would
get a constructor taking a '_Ios::_Index' which is used with
appropriate arguments in the definition of the standard stream objects
and which calls the corresponding constructor of 'basic_ios'.

Since the stream hierarchy is now robust against double initialization
when using the constructors taking '_Ios::_Index' as arguments the
nifty counter can simply use placement new to double initialize the
object. Still, all objects are explicitly initialized, including the
'ios_base' object.

I prefer the approach where the nifty counter is used but the double
initialization is prevented by actually changing the type of the
standard stream objects:

  // stream-objects.cpp
  #include <istream>
  #include <ostream>

  namespace std {
    char cin[sizeof(std::istream)];
    char cout[sizeof(std::ostream)];
    char cerr[sizeof(std::ostream)];
    char clog[sizeof(std::ostream)];
  }

The stream objects are then simply constructed with the normal
constructor and placement new by the nifty counter.

Of course, the drawback of this approach is that it makes some
assumptions which are not relyable (while the other approach makes
no such assumptions):
- The alignment of the character arrays may be different than the
  alignment of the corresponding stream objects.
- This only works if there is no form of mangling of the variable
  names which depends on the type.

Both approaches are viable according to the standard (for the second
this is, of course, only the case if the assumptions are true for the
compiler and linker) and both completely initialize all involved
objects, i.e. they both allow a free standing 'ios_base' object.

>> The next two sentences impose restrictions on the user of the class,
>> not on the implementer. These two sentences give the implementer the
>> freedom to leave the ios_base members indeed indeterminate. Of course,
>> the implementer can have setup ios_base properly or least in an
>> destructible state (e.g. be zero initializing the callback list and
>> the pword()/iword() members).
>
> You're talking about what an implementor can do in a favorable
> environment. That's not always possible.

I don't think that the above code took advantage of anything which
is not required by the standard anyway. Of course, this makes the
unrealistic assumption that the compiler is conforming to the
standard. :-) Seriously, I don't think I take advantage of anything
which is not provided by a normal compiler. Actually, the only two
features I take advantage of in the above code are that POD members
are left untouched are during construction and that objects with
static storage duration are set to zero before any constructor is
executed (for them). I can imagine that a compiler might inject code
to fill an object with some odd bit pattern prior to object
construction. If this is the case, it becomes necessary to save the
state of the standard stream object outside of the objects when
using the nifty counter object - leaving the object uninitialized
does not help because the compiler would just fill the object with
some bit pattern.

Of course:

>>                               Which technique is chosen depends on
>> whether the implementation accepts double construction of the
>> predefined objects: if it does, it will probably not allocate any
>> memory and may crash upon destruction. If the double construction is
>> prevented by some approach, the ios_base constructor can actually
>> properly setup the object.
>
> True, but you haven't won much. Instead of having a class that
> always causes trouble when used alone (as warned about in the C++
> Standard), you now have one that works right on some systems then
> crashes mysteriously when moved to another. Choose your poison.

whether it is a good idea to employ the above techniques is an
entirely different issue. My point is simply, that the standard does
not mandate that 'ios_base' is left uninitialized and it is not too
hard to always initialize it - even with unhelpful compiler/linker
implementations. BTW, the really tricky issue is that the standard
stream object should never be destroyed! (27.3 paragraph 2 last
sentence is rather explicit about this) Using the char-array trick
and only explicit initialization in the nifty counter has just this
effect because the compiler does not add any code for destroying the
objects: it actually has no idea that the arrays are intended to
hold any objects. Other approaches (like remembering pointers to the
standard stream objects in an array and not deleting any memory in
this case) are possible, too.

>> ios_base is obliged to have a non-trivial destructor. I don't think
>> that the restriction about indeterminate values is an obligation to
>> leave the members alone, i.e. ios_base can have a constructor which
>> initializes the members but the user cannot rely on it having such
>> a constructor.
>
> You said that before, but it's simply not true in many environments.
> And that was what was contemplated in the specification of ios_base.
> Remember, the problem has existed since the earliest days of C++,
> but is now made worse by spreading the double construction requirement
> across a template class and its base class -- then exposing the base
> class to the world.

Can you point out a flaw in the above code? (well, I didn't compile
it but it should be clear what is conceptually intended, i.e. I may
have made typos but I don't think I made a conceptual error)

>> Yes, I think ios_base can be made well-behaved if the standard
>> stream objects avoid double construction (or deal with possibly
>> existing setup somehow). The restriction is upon the user who
>> cannot expect ios_base to be initialized after merely default
>> constructing it, although it could be properly initialized.
>
> Uh, right. Doesn't that bring us back to square one?

There are two different issue here : whether it is possible to make
'ios_base' well-behaved (yes) and whether it is desirable to do so in
any implementation without the standard requiring it (no). As long as
the standard is not changed, the user has no guarantee that a free
standing 'ios_base' can work. However, I think I have shown that
the freedom given to the implementation is not necessary (well, it
may be necessary to retain binary compatibility...).

>> This is just one kind of areas where the C and C++ standards
>> use undefined behavior. Quite frequently undefined behavior is also
>> used if there would be additional run-time costs to diagnose problematic
>> behavior. For example, it would be trivial to setup some flag in
>> ios_base constructor which indicates whether 'init()' (on basic_ios)
>> was called and throw an exception when using any of ios_base members
>> if this flag is not set.
>
> Really? You mean a constructor that is supposed to do nothing at all
> can do just a little something and still survive double construction?

The constructor is not supposed to do nothing. However, it is not
guaranteed to do anything. These two statements are not equivalent:
while the implementer can choose to make 'ios_base' well-behaved,
the user cannot rely on it being well-behaved. The constructor is
*allowed* to do nothing.

> I've thought about that idea and dismissed it a dozen times over the
> past ten years or so.

Since your implementation does what the standard says, there is no
reason to change it. Whether the standard says what it should is a
different issue, though. I don't think it does and I think it can be
improved to give the user guarantees it currently does not give. As
long as the standard is not changed, failing as dramatically as
possible when the user misuses 'ios_base' is the best you can do, i.e.
there are even good reason *not* "improve" the current implementation.

Of course, my arguing is somewhat schizophrenic: on the one hand I
show how the IOStreams classes can be implemented and become
well-behaved, on the other hand I argue not to do it. I would drop
arguing against the improvement once the standard is changed... Of
course, when the standard should be changed in this area, everybody
has to be positively convinced that the change is the right thing.

> Overall, iostreams presents a decent interface to the world.

Indeed! It is IMO much easier to use than the corresponding classes
in other language (e.g. C# and Java). But then, maybe I would think
differently if I knew them as well as I know IOStreams :-)

> The
> mistake lay in having the C++ Standard expose too much of the
> innards to the world.

We definitely agree on this!
--
<mailto:dietmar_kuehl@yahoo.com> <http://www.dietmar-kuehl.de/>
<http://www.eai-systems.com> - Efficient Artificial Intelligence

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: eldiener_no_spam_here@earthlink.net (Edward Diener No Spam)
Date: Thu, 30 Jun 2005 05:43:57 GMT
Raw View
James Kanze wrote:
> P.J. Plauger wrote:
>
>  >>                                Of course, people who were
>  >>involved in the standardization of clause 22 know that it is
>  >>riddled with problems but this is not that obvious to users...
>
>  > Exactly. And this is the point James Kanze tried to make, I
>  > think.
>
> Well, my point had nothing to do with clause 22 (but I'll go
> along with Dietmar's statement about it).  It was said that the
> design of iostreams sucks, and that's a statement I just can't
> go along with.  There are a lot of good things in it.  There are
> also some things that I find less good, but that I can
> understand, given the context.
>
> And then, there are things which I really can't explain.  The
> separation of the orginal ios into ios_base and basic_ios, for
> example.  Or why locale dependant code translation is not a
> filtering streambuf.  Or for that matter, why the templates to
> begin with.  What can I do with a basic_istream or a
> basic_ostream instantiated on anything other than char or
> wchar_t?

Suppose a new basic character type were added in the future. Having
templates for IOStreams makes it much easier to deal with that situation
than having different non-templated classes.

A user is allowed to roll their own character type as a class. Using
templates again makes it easier to integrate that into IOStreams.

Of course I realize that IOStreams may not have the functionality to
deal with certain character types, but the idea was surely that it would
have a fighting chance to do so, and any future additions to it should
give it an even better chance in the future.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: dietmar_kuehl@yahoo.com (Dietmar Kuehl)
Date: Thu, 30 Jun 2005 14:54:30 GMT
Raw View
Edward Diener No Spam wrote:
> James Kanze wrote:
>> And then, there are things which I really can't explain.  The
>> separation of the orginal ios into ios_base and basic_ios, for
>> example.

If it had been done right, it would have been a good thing. As it is
now, it is a pain for the implementer and a minor trap for the user
(you can't create a free standing 'ios_base' object but this is the
whole impact on the user). It is a somewhat unfortunate design but
not that bad.

>> Or why locale dependant code translation is not a
>> filtering streambuf.

Now, this is a major problem because currently everybody who wants
to process wide characters with some custom external resource (e.g.
when sending Unicode over a socket as is conventional on the
Internet) has to cope with this non-trivial conversion stuff
(although my guess is that most people don't do it the way the
standard has laid out). If we had a filtering stream buffer which
converts wide characters to narrow characters the task of creating
a stream buffer for a custom external resource would only have to
deal with bytes and not with characters. In fairness with the C++
committee it is worth noting that we came up with the notion of
filtering stream buffers only after standard was essentially
finalized if I remember correctly. Also, I think there is a proposal
to add just such a filtering stream buffer. At least I remember that
someone (I think Matt) was talking about such a proposal.

>> Or for that matter, why the templates to
>> begin with.  What can I do with a basic_istream or a
>> basic_ostream instantiated on anything other than char or
>> wchar_t?

You can easily define a custom input or output operator which works
with all character types for which there are stream types. I think
this is the major motivation to turn the stream types into templates.
I think, using templates for the stream types was the right decision.
Some aspects of the realization are, however, questionable. In
general, I think the fundamental design decisions for streams
(separation of actual streaming and formatting, i.e. the stream and
stream buffer hierarchies; allowing customization of formatting, i.e.
locales; using templates for the character type; allowing association
of data with stream objects, i.e. iword()/pword()) are generally
right and provide a decent interface. However, there are many minor
design decisions which were lacking the experience we have now.

> Suppose a new basic character type were added in the future.

Which is not that unlikely: "uchar_t" for a 32 bit character type
for accommodation of Unicode characters ("wchar_t" cannot portably
because it is only guaranteed to have 16 bits) is a good contender.


> A user is allowed to roll their own character type as a class. Using
> templates again makes it easier to integrate that into IOStreams.

However, I have considerable doubts that there are really many users
which had done something like this!

> Of course I realize that IOStreams may not have the functionality to
> deal with certain character types, but the idea was surely that it would
> have a fighting chance to do so, and any future additions to it should
> give it an even better chance in the future.

You think anybody dares to touch IOStreams and extend them...?
--
<mailto:dietmar_kuehl@yahoo.com> <http://www.dietmar-kuehl.de/>
<http://www.eai-systems.com> - Efficient Artificial Intelligence

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: kanze@gabi-soft.fr
Date: Thu, 30 Jun 2005 09:55:16 CST
Raw View
Edward Diener No Spam wrote:
> James Kanze wrote:
> > P.J. Plauger wrote:

> >  >>                                Of course, people who were
> >  >>involved in the standardization of clause 22 know that it is
> >  >>riddled with problems but this is not that obvious to users...

> >  > Exactly. And this is the point James Kanze tried to make, I
> >  > think.

> > Well, my point had nothing to do with clause 22 (but I'll go
> > along with Dietmar's statement about it).  It was said that
> > the design of iostreams sucks, and that's a statement I just
> > can't go along with.  There are a lot of good things in it.
> > There are also some things that I find less good, but that I
> > can understand, given the context.

> > And then, there are things which I really can't explain.
> > The separation of the orginal ios into ios_base and
> > basic_ios, for example.  Or why locale dependant code
> > translation is not a filtering streambuf.  Or for that
> > matter, why the templates to begin with.  What can I do with
> > a basic_istream or a basic_ostream instantiated on anything
> > other than char or wchar_t?

> Suppose a new basic character type were added in the future.
> Having templates for IOStreams makes it much easier to deal
> with that situation than having different non-templated
> classes.

Maybe.  The reason that I would prefer two separate classes for
text is that the desired semantics for the two are different.
If the new basic character type has still different semantics,
we still need a third different class.

The problem here isn't templates per se.  The problem is trying
to force two different things into a single template class,
instead of defining two different classes (and allowing the
implementors to use a template to implement the parts that are
common).  (Note that I can sort of see templates in a slightly
higher level class -- multibyte character sequences can use
either 8 or 16 bit characters, and I often use single byte
character sequences with 8 bit bytes, although 16 or 32 bits
would be more universal.  But before we start talking about
templating such things, we first have to decide exactly what
they are to do.)

If there is a problem with regards to templates, it is the same
problem Java has with regards to virtual functions.  Just making
every function virtual doesn't mean that any Johnny come lately
can derive from the class, and customize it in any way he feels
like it.  You have to design for inheritance for the
customization to work.  Similarly, just making a class a
template, and making everything depend on a lot of typedefs and
static functions in a traits class doesn't guarantee unlimited
customization.  Again, you have to design for it, and consider
all of the possible customization scenarios.  If there were
specific customization scenarios considered, I'd be interested
in hearing about it, and what they were.

> A user is allowed to roll their own character type as a
> class.

Have you tried it?  The amount of work needed to make it usable
(the facets, etc.) is phenominal.

> Using templates again makes it easier to integrate that into
> IOStreams.

Or harder -- the problem is that each new type typically have
distinctive semantics.  (Otherwise, why bother?)

> Of course I realize that IOStreams may not have the
> functionality to deal with certain character types,

I've not been able to find it, but I presume that there are some
restrictions.  I doubt that something like std::basic_istream<
std::vector< MyClass > > can be made to work, and I see no
reason why an implementation should be required to support it.
(For that matter, I can't really see what "work" means in this
case.)

In practice, I suspect that there is an implicit restriction
that the iostream classes can only be instantiated over char and
wchar_t.  The amount of work needed to make it work for other
types is just too high.  We've introduced a lot of complexity
for the implementers, for no pratical gain for the users.  Not a
good trade off, IMHO.

> but the idea was surely that it would have a fighting chance
> to do so, and any future additions to it should give it an
> even better chance in the future.

Planning to provide for something you don't know nor understand
doesn't work.  Even today, I'm not sure exactly what is needed;
handling multiple international character sets is still largely
experimental.  What is certain is that just throwing a lot of
templates at the problem isn't going to help.

--
James Kanze                                           GABI Software
Conseils en informatique orient   e objet/
                   Beratung in objektorientierter Datenverarbeitung
9 place S   mard, 78210 St.-Cyr-l'   cole, France, +33 (0)1 30 23 00 34


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: "P.J. Plauger" <pjp@dinkumware.com>
Date: 30 Jun 2005 15:30:02 GMT
Raw View
"Dietmar Kuehl" <dietmar_kuehl@yahoo.com> wrote in message
news:3ihsjvFlf4lqU1@individual.net...

> Now, this is a major problem because currently everybody who wants
> to process wide characters with some custom external resource (e.g.
> when sending Unicode over a socket as is conventional on the
> Internet) has to cope with this non-trivial conversion stuff
> (although my guess is that most people don't do it the way the
> standard has laid out). If we had a filtering stream buffer which
> converts wide characters to narrow characters the task of creating
> a stream buffer for a custom external resource would only have to
> deal with bytes and not with characters. In fairness with the C++
> committee it is worth noting that we came up with the notion of
> filtering stream buffers only after standard was essentially
> finalized if I remember correctly.

Well, actually I proposed something along those lines, but it
got trundled over quite early.

>                                   Also, I think there is a proposal
> to add just such a filtering stream buffer. At least I remember that
> someone (I think Matt) was talking about such a proposal.

Yes, it's from our CoreX library. See the online manual at our
web site.

P.J. Plauger
Dinkumware, Ltd.
http://www.dinkumware.com


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: technews@kangaroologic.com ("Jonathan Turkanis")
Date: Thu, 30 Jun 2005 19:27:29 GMT
Raw View
Dietmar Kuehl wrote:

>> James Kanze wrote:

>>> Or why locale dependant code translation is not a
>>> filtering streambuf.
>
> Now, this is a major problem because currently everybody who wants
> to process wide characters with some custom external resource (e.g.
> when sending Unicode over a socket as is conventional on the
> Internet) has to cope with this non-trivial conversion stuff
> (although my guess is that most people don't do it the way the
> standard has laid out). If we had a filtering stream buffer which
> converts wide characters to narrow characters the task of creating
> a stream buffer for a custom external resource would only have to
> deal with bytes and not with characters. In fairness with the C++
> committee it is worth noting that we came up with the notion of
> filtering stream buffers only after standard was essentially
> finalized if I remember correctly. Also, I think there is a proposal
> to add just such a filtering stream buffer. At least I remember that
> someone (I think Matt) was talking about such a proposal.

As PJP mentioned, the proposal is to standardize several components from the
Dinkumware CoreX library. The Boost Iostreams library contains a component
code_converter which generalizes the dinkumware component wbuffer_convert. It's
a "device adapter" which takes a narrow character device and turns it into a
wide character device:

      typedef code_converter<mapped_file_source> wmapped_file_source;

This defines a device which reads wide characters from a mapped_file_source,
using a codecvt fetched from the global locale. You can also specify a custom
codecvt facet:

      typedef code_converter<mapped_file_source, utf8_codecvt >
utf8_mapped_file_source;

This defines a device which reads wide characters from a mapped_file_source,
using an instance of utf8_codecvt.

If you specify std::streambuf as the first template argument, you get a
component which is basically equivalent to wbuffer_convert.

Jonathan



---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: pjp@dinkumware.com ("P.J. Plauger")
Date: Tue, 28 Jun 2005 06:41:45 GMT
Raw View
<martinfrompi@yahoo.co.uk> wrote in message
news:1119863921.823781.32950@f14g2000cwb.googlegroups.com...

> "P.J. Plauger" wrote:
>> ""Krzysztof    elechowski"" <yecril@bluebottle.com> wrote in message
>> news:d9mg2u$mr2$1@sklad.atcom.net.pl...
>>
>> > The Standard says that you cannot derive from ios_base otherwise than
>> > via
>> > basic_ios.
>> > [snip]
>> > Microsoft Visual C++ does not detect it and my program breaks with an
>> > access error (Microsoft's term for a bus error) in ~ios_base().
>> >
>> > If I had designed such a confusing class and refused to make it more
>> > foolproof, I would probably have been fired.
>>
>> Probably. I agree that the design sucks.
>>
>> P.J. Plauger
>
> Now then.  Are you saying that the external interface (as specified in
> the Standard) is poor,

Yes.

>                       or are you saying that the internal design of
> the implementation provided with Microsoft Visual C++ could do with
> improvement?

I don't know how. We're terribly constrained by the C++ Standard,
as are other implementors.

> If you mean the former, I don't think you actually /are/ agreeing with
> Krzysztof, as I think he means the latter.

I seem to have difficulty communicating with the OP on other topics,
so I wouldn't be surprised if he meant it as a personal slap and
I missed his intent. The moderators let it through, however, (and
they're good at seeing insults in some of the things I write) so he
couldn't possibly have been aiming a personal attack at me.

>                                            Anyway, what is it about
> the specification that makes it difficult to implement robustly?

The design is based on the original iostreams from AT&T, which lets
you use streams during static construction and destruction time.
This is a difficult feat since you can't generally ensure that the
streams are constructed early enough in the normal course of static
construction. The "nifty counter trick" employed requires that the
iostreams objects (such as cin and cout) are *not* constructed
in their constructors and are *not* destroyed in their destructors.
Rather, the construction and destruction of a separate object
performs these essential operations. Thus, iostreams objects must
suffer double construction and double destruction, with the order
of the the constructor pairs and destructor pairs unspecified.
And the usual life-to-death contract doesn't apply, at least in
critical places within these objects.

The C++ Standard enshrines the nifty counter trick in detailed
specifications, so modern implementations must support this
trick even when it's not needed -- and it's not with more than
one widely used linker. But to make matters worse, Standard C++
iostreams are templatized. In the process of debugging this
enhancement, the C++ committee decided to split the old ios
class (the base of all iostreams objects) into a templatized
basic_ios<CharT> and a common non-template class ios_base.
For the nifty counter trick to work, class ios_base *must*
be properly initialized by basic_ios<CharT>(!)

So here we have a class that is *required* to be left in an
uninitialized state, yet really requires a nontrivial
destructor. If you have the temerity to construct an ios_base
object that is not a subobject of a basic_ios<CharT> object,
your chances of bombing out in the ios_base destructor are
very high.

BTW, there was a thread along these lines several months ago,
I forget in which newsgroup, where more than one person
(possibly including the OP of this thread, but I no longer
remember clearly) asserted that ios_base could be made more
robust. I spent about half a day trying the various schemes
proposed and found fault with all of them. But please don't
ask me to reprise all the subtleties. I get a headache every
time I have to revisit this code.

Our implementation works and it conforms.

P.J. Plauger
Dinkumware, Ltd.
http://www.dinkumware.com


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: dietmar_kuehl@yahoo.com (Dietmar Kuehl)
Date: Tue, 28 Jun 2005 14:08:17 GMT
Raw View
P.J. Plauger wrote:
> <martinfrompi@yahoo.co.uk> wrote in message
> news:1119863921.823781.32950@f14g2000cwb.googlegroups.com...
>> Now then.  Are you saying that the external interface (as specified in
>> the Standard) is poor,
>
> Yes.

The interaction between ios_base and basic_ios is definitely poor!
On the other hand, I don't think that it is impossible to create a
stable interface to ios_base. The only drawback I can see so far is
that ios_base might sometimes do some extra work compared to an
implementation which takes advantage of the leeway granted by the
requirements on using these classes.

>>                       or are you saying that the internal design of
>> the implementation provided with Microsoft Visual C++ could do with
>> improvement?

If P.J.Plauger thought that the library shipping with MSVC++ should
be improved, he would probably just improve it (although there are
probably rather severe constraints with respect to binary compatibility).

> I don't know how. We're terribly constrained by the C++ Standard,
> as are other implementors.

Of course, these constraints are often essential to users as they
provide the necessary guarantees for programs working with multiple
implementations. After all, the standards are not made for the
implementers but for the users. It is the implementers task to avoid
unbearable restrictions in the standard but to find an implementation
obeying those which made it into the standard.

>>                                            Anyway, what is it about
>> the specification that makes it difficult to implement robustly?
>
> The design is based on the original iostreams from AT&T, which lets
> you use streams during static construction and destruction time.
> This is a difficult feat since you can't generally ensure that the
> streams are constructed early enough in the normal course of static
> construction. The "nifty counter trick" employed requires that the
> iostreams objects (such as cin and cout) are *not* constructed
> in their constructors and are *not* destroyed in their destructors.
> Rather, the construction and destruction of a separate object
> performs these essential operations.

I agree thus far but not for general streams. This only applies to
the eight predefined stream objects (cin, cout, cerr, and clog plus
their wide-character counterparts).

> Thus, iostreams objects must
> suffer double construction and double destruction, with the order
> of the the constructor pairs and destructor pairs unspecified.
> And the usual life-to-death contract doesn't apply, at least in
> critical places within these objects.

I disagree with these statements though. This is not a necessary
implication. First of all, there are other implementation techniques
which guarantee early construction/late destruction of objects than
the nifty counter approach. For example, you can put the corresponding
objects into a separate shared object and construct them upon loading
of this object. Since the object can only be referenced after the
shared object where it is located is loaded, this guarantees that the
object is constructed sufficiently early and, correspondingly,
destructed sufficiently late. Other techniques include the use of
named sections and start-up/finalization code which deals with these
named sections separately (i.e. constructs them prior to all other
sections and destructs them after all other sections).

Even with the nifty counter trick it is possible to prevent double
construction/destruction - although not portably (but this doesn't
matter since the standard C++ library is part of the implementation
and can do whatever is appropriate on the respective system; also,
I think this approach worked with all implementation I had available
when I last tested it): there is no need to define the stream objects
with the correct type because object of different types get the same
name for the linker (which is reasonable as object cannot be overloaded
and thus don't need any mangling). Thus, the stream objects are
declared as some kind of stream in the headers included by user code
but they can still be defined e.g. as character arrays of sufficient
size. Since constructors and destructors of 'char's are no-ops the only
construction and destruction suffered by these objects is the placement
construction/destruction of the nifty counter.

> The C++ Standard enshrines the nifty counter trick in detailed
> specifications, so modern implementations must support this
> trick even when it's not needed -- and it's not with more than
> one widely used linker.

What the standard enshrines is that there has to be an
'std::ios_base::Init' class whose destructor flushes the predefined
streams if its internal counter drops to zero during destruction.
This class *can* be used for construction of the predefined stream
objects but I don't think there is a restrictions which prevents
constructions of the predefined stream objects prior to construction
of the first 'Init' object. Correspondingly for destruction.

> But to make matters worse, Standard C++
> iostreams are templatized. In the process of debugging this
> enhancement, the C++ committee decided to split the old ios
> class (the base of all iostreams objects) into a templatized
> basic_ios<CharT> and a common non-template class ios_base.
> For the nifty counter trick to work, class ios_base *must*
> be properly initialized by basic_ios<CharT>(!)

I disagree. 'ios_base' may initialize itself properly in a way
which allows independent construction and destruction (I'm, however,
not really sure whether I have realized this in the publically
available version of my own implementation; after all, the standard
makes no guarantees object such objects...).

> So here we have a class that is *required* to be left in an
> uninitialized state, yet really requires a nontrivial
> destructor. If you have the temerity to construct an ios_base
> object that is not a subobject of a basic_ios<CharT> object,
> your chances of bombing out in the ios_base destructor are
> very high.

Of course, according to the standard this is fine as it yields
undefined behavior and it is actually better if a program crashes
visibly on all platforms rather than creating a problem during
porting to another platform!

> Our implementation works and it conforms.

Which is actually the important aspect. In fact, I think it is a
service to the user to generate an error as soon as he steps into
undefined behavior. Preventing misuses of 'ios_base' to create an
obvious error is the wrong direction as it hides a problem and
effectively locks the user into an implementation and makes porting
to another platform harder. Of course, from an implementers point
of view things can be different at times... The real question is
whether it is worth fixing IOStreams in the next standard and making
it more robust because fixing individual implementations is a
disservice anyway. Since there are many complaints about IOStreams
it could be the question where to start. On the other hand, I'm
currently using .Net's I/O facilities and I really prefer IOStreams
by a wide margin (I remember a similar feeling when using Java's
I/O facilities but this is actually long ago).
--
<mailto:dietmar_kuehl@yahoo.com> <http://www.dietmar-kuehl.de/>
<http://www.eai-systems.com> - Efficient Artificial Intelligence

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: kanze@gabi-soft.fr
Date: 28 Jun 2005 14:20:02 GMT
Raw View
martinfrompi@yahoo.co.uk wrote:
> "P.J. Plauger" wrote:
> > ""Krzysztof    elechowski"" <yecril@bluebottle.com> wrote in
> > message news:d9mg2u$mr2$1@sklad.atcom.net.pl...

> > > The Standard says that you cannot derive from ios_base
> > > otherwise than via basic_ios.
> > > [snip]
> > > Microsoft Visual C++ does not detect it and my program
> > > breaks with an access error (Microsoft's term for a bus
> > > error) in ~ios_base().

> > > If I had designed such a confusing class and refused to
> > > make it more foolproof, I would probably have been fired.

> > Probably. I agree that the design sucks.

> Now then.  Are you saying that the external interface (as
> specified in the Standard) is poor, or are you saying that the
> internal design of the implementation provided with Microsoft
> Visual C++ could do with improvement?

> If you mean the former, I don't think you actually /are/
> agreeing with Krzysztof, as I think he means the latter.
> Anyway, what is it about the specification that makes it
> difficult to implement robustly?

Then what he means and what he says are two different things.
He specifically spoke of the design of the class.

In practice, although the case of basic_ios is perhaps extreme,
it's not rare for templates to derive from a base class which
only makes sense as a base class for that template.  It's
actually a common trick to avoid code bloat, although one can
reasonably ask the question whether it is appropriate (or used
appropriately) in the case of basic_ios/ios_base.  (There are
some pretty complex requirements for the iostream hierarchy,
like the fact that std::cout et al. have to be constructed
before they are constructed, which make things particularly
difficult for an implementor.  On the other hand, would you
accept the fact that you could not use std::cerr in the
constructor of a static object?)

I actually disagree with both the original poster and Plauger
here.  While there are a number of things I don't like about the
current iostreams, I find the fundamental design of the
classical iostreams very good.  Taking into account 1) the
requirements specifications (including the fact that they some
of the stream objects must be usable during static construction)
and 2) the available technologies and known techniques at the
time (before 1990).  In this particular case, I don't think that
the standards committee improved things, but I still find that
if you ignore the template mess, and just consider istream,
ostream, ios and streambuf, you still have one of the best
examples I know of for explaining the different tradeoffs with
regards to virtual functions and function/operator overloading.

--
James Kanze                                           GABI Software
Conseils en informatique orient   e objet/
                   Beratung in objektorientierter Datenverarbeitung
9 place S   mard, 78210 St.-Cyr-l'   cole, France, +33 (0)1 30 23 00 34


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: "P.J. Plauger" <pjp@dinkumware.com>
Date: 28 Jun 2005 16:50:01 GMT
Raw View
"Dietmar Kuehl" <dietmar_kuehl@yahoo.com> wrote in message
news:3icvv3Fkre0rU1@individual.net...

> P.J. Plauger wrote:
>> <martinfrompi@yahoo.co.uk> wrote in message
>> news:1119863921.823781.32950@f14g2000cwb.googlegroups.com...
>>> Now then.  Are you saying that the external interface (as specified in
>>> the Standard) is poor,
>>
>> Yes.
>
> The interaction between ios_base and basic_ios is definitely poor!
> On the other hand, I don't think that it is impossible to create a
> stable interface to ios_base. The only drawback I can see so far is
> that ios_base might sometimes do some extra work compared to an
> implementation which takes advantage of the leeway granted by the
> requirements on using these classes.

I'm always willing to be educated, but I've tried many times over
the past decade and have yet to succeed.

>>>                       or are you saying that the internal design of
>>> the implementation provided with Microsoft Visual C++ could do with
>>> improvement?
>
> If P.J.Plauger thought that the library shipping with MSVC++ should
> be improved, he would probably just improve it (although there are
> probably rather severe constraints with respect to binary compatibility).

Thanks.

>> I don't know how. We're terribly constrained by the C++ Standard,
>> as are other implementors.
>
> Of course, these constraints are often essential to users as they
> provide the necessary guarantees for programs working with multiple
> implementations. After all, the standards are not made for the
> implementers but for the users. It is the implementers task to avoid
> unbearable restrictions in the standard but to find an implementation
> obeying those which made it into the standard.
>
>>>                                            Anyway, what is it about
>>> the specification that makes it difficult to implement robustly?
>>
>> The design is based on the original iostreams from AT&T, which lets
>> you use streams during static construction and destruction time.
>> This is a difficult feat since you can't generally ensure that the
>> streams are constructed early enough in the normal course of static
>> construction. The "nifty counter trick" employed requires that the
>> iostreams objects (such as cin and cout) are *not* constructed
>> in their constructors and are *not* destroyed in their destructors.
>> Rather, the construction and destruction of a separate object
>> performs these essential operations.
>
> I agree thus far but not for general streams. This only applies to
> the eight predefined stream objects (cin, cout, cerr, and clog plus
> their wide-character counterparts).

Right, these are the only streams that have mandated static
constructors.

>> Thus, iostreams objects must
>> suffer double construction and double destruction, with the order
>> of the the constructor pairs and destructor pairs unspecified.
>> And the usual life-to-death contract doesn't apply, at least in
>> critical places within these objects.
>
> I disagree with these statements though. This is not a necessary
> implication. First of all, there are other implementation techniques
> which guarantee early construction/late destruction of objects than
> the nifty counter approach...

Yes, there are alternate techniques. I said so too. But the
C++ Standard still enshrines machinery used for the traditional
approach, and *this* is what causes the problem.

> Even with the nifty counter trick it is possible to prevent double
> construction/destruction - although not portably...

Also true, but that *still* isn't the issue.

>> The C++ Standard enshrines the nifty counter trick in detailed
>> specifications, so modern implementations must support this
>> trick even when it's not needed -- and it's not with more than
>> one widely used linker.
>
> What the standard enshrines is that there has to be an
> 'std::ios_base::Init' class whose destructor flushes the predefined
> streams if its internal counter drops to zero during destruction.
> This class *can* be used for construction of the predefined stream
> objects but I don't think there is a restrictions which prevents
> constructions of the predefined stream objects prior to construction
> of the first 'Init' object. Correspondingly for destruction.

It also enshrines the following:

: 27.4.2.7 ios_base constructors/destructors
:
: ios_base();
:
: 1 Effects: Each ios_base member has an indeterminate value after
: construction. These members must be initialized by calling
: basic_ios::init. If an ios_base object is destroyed before these
: initializations have taken place, the behavior is undefined.
:
: ~ios_base()
:
: 2 Effects: Destroys an object of class ios_base. Calls each
: registered callback pair (fn,index) (27.4.2.6) as
: (*fn)(erase_event,*this,index) at such time that any ios_base
: member function called from within fn has well defined results.

As I said, ios_base is *obliged* to have an ineffectual constructor
and a nontrivial destructor. The people who get in trouble are the
ones who try to make a freestanding ios_base object, in the teeth
of this noxious restriction.

>> But to make matters worse, Standard C++
>> iostreams are templatized. In the process of debugging this
>> enhancement, the C++ committee decided to split the old ios
>> class (the base of all iostreams objects) into a templatized
>> basic_ios<CharT> and a common non-template class ios_base.
>> For the nifty counter trick to work, class ios_base *must*
>> be properly initialized by basic_ios<CharT>(!)
>
> I disagree. 'ios_base' may initialize itself properly in a way
> which allows independent construction and destruction (I'm, however,
> not really sure whether I have realized this in the publically
> available version of my own implementation; after all, the standard
> makes no guarantees object such objects...).

Really? See above.

>> So here we have a class that is *required* to be left in an
>> uninitialized state, yet really requires a nontrivial
>> destructor. If you have the temerity to construct an ios_base
>> object that is not a subobject of a basic_ios<CharT> object,
>> your chances of bombing out in the ios_base destructor are
>> very high.
>
> Of course, according to the standard this is fine as it yields
> undefined behavior and it is actually better if a program crashes
> visibly on all platforms rather than creating a problem during
> porting to another platform!

I mostly agree.

>> Our implementation works and it conforms.
>
> Which is actually the important aspect. In fact, I think it is a
> service to the user to generate an error as soon as he steps into
> undefined behavior.

Well, yes, but... The C and C++ Standards usually leave undefined
just that behavior that's hard to diagnose.

>                   Preventing misuses of 'ios_base' to create an
> obvious error is the wrong direction as it hides a problem and
> effectively locks the user into an implementation and makes porting
> to another platform harder. Of course, from an implementers point
> of view things can be different at times... The real question is
> whether it is worth fixing IOStreams in the next standard and making
> it more robust because fixing individual implementations is a
> disservice anyway. Since there are many complaints about IOStreams
> it could be the question where to start...

Indeed. This is a dark corner of a complex piece of machinery.
We're probably better off leaving it alone.

P.J. Plauger
Dinkumware, Ltd.
http://www.dinkumware.com


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: pjp@dinkumware.com ("P.J. Plauger")
Date: Tue, 28 Jun 2005 16:49:47 GMT
Raw View
<kanze@gabi-soft.fr> wrote in message
news:1119962053.056501.78040@z14g2000cwz.googlegroups.com...

> In practice, although the case of basic_ios is perhaps extreme,
> it's not rare for templates to derive from a base class which
> only makes sense as a base class for that template.  It's
> actually a common trick to avoid code bloat, although one can
> reasonably ask the question whether it is appropriate (or used
> appropriately) in the case of basic_ios/ios_base.  (There are
> some pretty complex requirements for the iostream hierarchy,
> like the fact that std::cout et al. have to be constructed
> before they are constructed, which make things particularly
> difficult for an implementor.  On the other hand, would you
> accept the fact that you could not use std::cerr in the
> constructor of a static object?)
>
> I actually disagree with both the original poster and Plauger
> here.  While there are a number of things I don't like about the
> current iostreams, I find the fundamental design of the
> classical iostreams very good.

I wasn't criticizing iostreams as a whole, just the particular
constraints on its ios_base sub-sub-component. We would all
have been better off if the C++ Standard had said what the
eight standard streams had to do, without all the excessive
detail about how they might pull it off.

>                                Taking into account 1) the
> requirements specifications (including the fact that they some
> of the stream objects must be usable during static construction)
> and 2) the available technologies and known techniques at the
> time (before 1990).  In this particular case, I don't think that
> the standards committee improved things, but I still find that
> if you ignore the template mess, and just consider istream,
> ostream, ios and streambuf, you still have one of the best
> examples I know of for explaining the different tradeoffs with
> regards to virtual functions and function/operator overloading.

I mostly agree, but I believe that the C++ committee made things
worse by teasing out ios_base from basic_ios<CharT> and then
overspecifying their interaction.

P.J. Plauger
Dinkumware, Ltd.
http://www.dinkumware.com


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: dietmar_kuehl@yahoo.com (Dietmar Kuehl)
Date: Tue, 28 Jun 2005 20:25:42 GMT
Raw View
P.J. Plauger wrote:
> "Dietmar Kuehl" <dietmar_kuehl@yahoo.com> wrote in message
> news:3icvv3Fkre0rU1@individual.net...
>> What the standard enshrines is that there has to be an
>> 'std::ios_base::Init' class whose destructor flushes the predefined
>> streams if its internal counter drops to zero during destruction.
>> This class *can* be used for construction of the predefined stream
>> objects but I don't think there is a restrictions which prevents
>> constructions of the predefined stream objects prior to construction
>> of the first 'Init' object. Correspondingly for destruction.
>
> It also enshrines the following:
>
> : 27.4.2.7 ios_base constructors/destructors
> :
> : ios_base();
> :
> : 1 Effects: Each ios_base member has an indeterminate value after
> : construction. These members must be initialized by calling
> : basic_ios::init. If an ios_base object is destroyed before these
> : initializations have taken place, the behavior is undefined.

The first sentence states that the user cannot rely on any particular
values when accessing constructed ios_base object because they need
not be initialized. I don't think that this sentence prevents the
implementation to initialize ios_base members. After all, I cannot
imagine any testsuite which can detect that the indeterminate values
are actually values which make sense to the implementation. In fact,
the testsuite has to expect arbitrary values from ios_base members
and thus cannot tell whether they are initialized or not even though
some values may look as if they result from some form of initialization.

The next two sentences impose restrictions on the user of the class,
not on the implementer. These two sentences give the implementer the
freedom to leave the ios_base members indeed indeterminate. Of course,
the implementer can have setup ios_base properly or least in an
destructible state (e.g. be zero initializing the callback list and
the pword()/iword() members). Which technique is chosen depends on
whether the implementation accepts double construction of the
predefined objects: if it does, it will probably not allocate any
memory and may crash upon destruction. If the double construction is
prevented by some approach, the ios_base constructor can actually
properly setup the object.

> : ~ios_base()
> :
> : 2 Effects: Destroys an object of class ios_base. Calls each
> : registered callback pair (fn,index) (27.4.2.6) as
> : (*fn)(erase_event,*this,index) at such time that any ios_base
> : member function called from within fn has well defined results.
>
> As I said, ios_base is *obliged* to have an ineffectual constructor
> and a nontrivial destructor.

ios_base is obliged to have a non-trivial destructor. I don't think
that the restriction about indeterminate values is an obligation to
leave the members alone, i.e. ios_base can have a constructor which
initializes the members but the user cannot rely on it having such
a constructor.

> The people who get in trouble are the
> ones who try to make a freestanding ios_base object, in the teeth
> of this noxious restriction.

I agree wholeheartedly with this statement and I'm not sure whether
it is helping these people if the problem is masked by an
implementation which behaves like some users might expect that it
behaves. There is no guarantee and producing odd behavior (like e.g.
crashing during destruction) is a good approach to indicate that
something is wrong.

>>> But to make matters worse, Standard C++
>>> iostreams are templatized. In the process of debugging this
>>> enhancement, the C++ committee decided to split the old ios
>>> class (the base of all iostreams objects) into a templatized
>>> basic_ios<CharT> and a common non-template class ios_base.
>>> For the nifty counter trick to work, class ios_base *must*
>>> be properly initialized by basic_ios<CharT>(!)
>>
>> I disagree. 'ios_base' may initialize itself properly in a way
>> which allows independent construction and destruction (I'm, however,
>> not really sure whether I have realized this in the publically
>> available version of my own implementation; after all, the standard
>> makes no guarantees object such objects...).
>
> Really? See above.

Yes, I think ios_base can be made well-behaved if the standard
stream objects avoid double construction (or deal with possibly
existing setup somehow). The restriction is upon the user who
cannot expect ios_base to be initialized after merely default
constructing it, although it could be properly initialized.

>>> Our implementation works and it conforms.
>>
>> Which is actually the important aspect. In fact, I think it is a
>> service to the user to generate an error as soon as he steps into
>> undefined behavior.
>
> Well, yes, but... The C and C++ Standards usually leave undefined
> just that behavior that's hard to diagnose.

This is just one kind of areas where the C and C++ standards
use undefined behavior. Quite frequently undefined behavior is also
used if there would be additional run-time costs to diagnose problematic
behavior. For example, it would be trivial to setup some flag in
ios_base constructor which indicates whether 'init()' (on basic_ios)
was called and throw an exception when using any of ios_base members
if this flag is not set. Of course, this approach would burdon
ios_base operations and make it even harder to create an efficient
IOStream implementation than is already the case. Leaving things
undefined allows "nice" implementations diagnosing the problem while
possibly being slower and fast implementations which assume that the
user obeys the rules.

BTW, it is not a completely odd idea to use a freestanding ios_base
object: for example, the num_put and num_get member functions take
ios_base objects for the formatting and possibly take additional
arguments for the fill character (in the case of num_put) or state
flags (in the case of num_get). This does not really make any sense
if each ios_base object is really some 'basic_ios<cT, traits>'
instantiation because it would be more reasonable to pass the
'basic_ios<cT, traits>' object in the first place and thus just one
rather than multiple parameters. Of course, people who were involved
in the standardization of clause 22 know that it is riddled with
problems but this is not that obvious to users...
--
<mailto:dietmar_kuehl@yahoo.com> <http://www.dietmar-kuehl.de/>
<http://www.eai-systems.com> - Efficient Artificial Intelligence

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: dietmar_kuehl@yahoo.com (Dietmar Kuehl)
Date: Tue, 28 Jun 2005 20:25:13 GMT
Raw View
P.J. Plauger wrote:
> I mostly agree, but I believe that the C++ committee made things
> worse by teasing out ios_base from basic_ios<CharT> and then
> overspecifying their interaction.

Especially as it is impossible (at least for users) to create an
ios_base which is not a basic_ios object anyway! Factoring the
character independent stuff into a base class would have been
possible for each implememtation anyway.
--
<mailto:dietmar_kuehl@yahoo.com> <http://www.dietmar-kuehl.de/>
<http://www.eai-systems.com> - Efficient Artificial Intelligence

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: kkz@duch.mimuw.edu.pl (Christopher Conrade Zseleghovski)
Date: Tue, 28 Jun 2005 22:00:00 GMT
Raw View
Dietmar Kuehl <dietmar_kuehl@yahoo.com> wrote:
> P.J. Plauger wrote:
>> I mostly agree, but I believe that the C++ committee made things
>> worse by teasing out ios_base from basic_ios<CharT> and then
>> overspecifying their interaction.
>
> Especially as it is impossible (at least for users) to create an
> ios_base which is not a basic_ios object anyway! Factoring the
> character independent stuff into a base class would have been
> possible for each implememtation anyway.

It should not be possible but it is possible.  I have actually run into
the access error I described, that is how my question appeared.

Chris

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: dietmar_kuehl@yahoo.com (Dietmar Kuehl)
Date: Wed, 29 Jun 2005 02:01:21 GMT
Raw View
Christopher Conrade Zseleghovski wrote:
> Dietmar Kuehl <dietmar_kuehl@yahoo.com> wrote:
>> Especially as it is impossible (at least for users) to create an
>> ios_base which is not a basic_ios object anyway! Factoring the
>> character independent stuff into a base class would have been
>> possible for each implememtation anyway.
>
> It should not be possible but it is possible.  I have actually run into
> the access error I described, that is how my question appeared.

Well, technically it is possible to create an ios_base object but you
can't destruct it. This is what the standard says. The standard puts
a restriction upon the use of ios_base object which is essentially
that it is initialized by 'basic_ios<...>::init()' when destructing
the object. This can only be achieved by using a basic_ios object,
i.e. if you create an ios_base object you are not obeying the rules:
you do something which is impossible for you to do [while obeying
rules]. Of course, you get what you deserve (e.g. an access error
but possibly much harder to debug situation) if you don't obey the
rules. In C++ the primary punishment for failing to follow the rules
is "undefined behavior" which is pretty bad because you can't know
what you will really get. Often it will just be an "access error"
or some useful behavior but it can also result in loss of data due
to erase or overwritten files.
--
<mailto:dietmar_kuehl@yahoo.com> <http://www.dietmar-kuehl.de/>
<http://www.eai-systems.com> - Efficient Artificial Intelligence

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: yecril@bluebottle.com ("Krzysztof Zelechowski")
Date: Wed, 29 Jun 2005 14:51:17 GMT
Raw View
Uzytkownik "Dietmar Kuehl" <dietmar_kuehl@yahoo.com> napisal w wiadomosci
news:3ie6diFl4197U1@individual.net...
> Christopher Conrade Zseleghovski wrote:
>> Dietmar Kuehl <dietmar_kuehl@yahoo.com> wrote:
>>> Especially as it is impossible (at least for users) to create an
>>> ios_base which is not a basic_ios object anyway! Factoring the
>>> character independent stuff into a base class would have been
>>> possible for each implememtation anyway.
>>
>> It should not be possible but it is possible.  I have actually run into
>> the access error I described, that is how my question appeared.
>
> Well, technically it is possible to create an ios_base object but you
> can't destruct it. This is what the standard says. The standard puts

But I can inherit from an ios_base and destroy the inherited object.

> a restriction upon the use of ios_base object which is essentially
> that it is initialized by 'basic_ios<...>::init()' when destructing
> the object. This can only be achieved by using a basic_ios object,
> i.e. if you create an ios_base object you are not obeying the rules:
> you do something which is impossible for you to do [while obeying
> rules]. Of course, you get what you deserve (e.g. an access error
> but possibly much harder to debug situation) if you don't obey the
> rules. In C++ the primary punishment for failing to follow the rules
> is "undefined behavior" which is pretty bad because you can't know
> what you will really get. Often it will just be an "access error"
> or some useful behavior but it can also result in loss of data due
> to erase or overwritten files.

I meant that these rules are a flaw in the design of the base class.  The
compiler should generate an error if it sees a class inheriting from
ios_base and not inheriting from basic_ios.  Think about the following
scheme: make the destructor private and make friends with basic_ios so that
I cannot inherit but basic_ios can.

If this is not possible, my derived class should not crash in the destructor
because the offending piece of code could be in some dark corner my customer
does not look into until five years from now, e.g. until it encounters a
keyword WON_HSARC in his input file.  Crashing does not help the developer
as compiler error messages do because only the compiler sees all my code,
the executing application need not.  Point.

Of course you can RTFM everyone, but C++ claims RTFMing and memorizing
everything is not that crucial because the compiler can handle it itself -
and it is an important advantage over C.  The destructor crash phenomenon
makes this claim invalid.

Christopher


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: kanze@gabi-soft.fr
Date: Wed, 29 Jun 2005 09:55:03 CST
Raw View
Dietmar Kuehl wrote:
> P.J. Plauger wrote:
> > I mostly agree, but I believe that the C++ committee made
> > things worse by teasing out ios_base from basic_ios<CharT>
> > and then overspecifying their interaction.

> Especially as it is impossible (at least for users) to create
> an ios_base which is not a basic_ios object anyway! Factoring
> the character independent stuff into a base class would have
> been possible for each implememtation anyway.

Not really.  A conforming program can tell the difference: in
particular, std::ios::fmtflags and std::wios::fmtflags must be
the same type.  Given the way the two classes are separated, I
presume that the intent and motivation for the separate,
non-template base, was to require that there be just one type.
IMHO, it seems like a lot of excess complication, for a very
small benefit, but that's just me.  (You and Plauger seem to
more or less agree, so at least I'm in good company.)

--
James Kanze                                           GABI Software
Conseils en informatique orient   e objet/
                   Beratung in objektorientierter Datenverarbeitung
9 place S   mard, 78210 St.-Cyr-l'   cole, France, +33 (0)1 30 23 00 34


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: pjp@dinkumware.com ("P.J. Plauger")
Date: Wed, 29 Jun 2005 14:55:04 GMT
Raw View
"Dietmar Kuehl" <dietmar_kuehl@yahoo.com> wrote in message
news:3idkmtFkt0pnU1@individual.net...

> P.J. Plauger wrote:
>> "Dietmar Kuehl" <dietmar_kuehl@yahoo.com> wrote in message
>> news:3icvv3Fkre0rU1@individual.net...
>>> What the standard enshrines is that there has to be an
>>> 'std::ios_base::Init' class whose destructor flushes the predefined
>>> streams if its internal counter drops to zero during destruction.
>>> This class *can* be used for construction of the predefined stream
>>> objects but I don't think there is a restrictions which prevents
>>> constructions of the predefined stream objects prior to construction
>>> of the first 'Init' object. Correspondingly for destruction.
>>
>> It also enshrines the following:
>>
>> : 27.4.2.7 ios_base constructors/destructors
>> :
>> : ios_base();
>> :
>> : 1 Effects: Each ios_base member has an indeterminate value after
>> : construction. These members must be initialized by calling
>> : basic_ios::init. If an ios_base object is destroyed before these
>> : initializations have taken place, the behavior is undefined.
>
> The first sentence states that the user cannot rely on any particular
> values when accessing constructed ios_base object because they need
> not be initialized. I don't think that this sentence prevents the
> implementation to initialize ios_base members. After all, I cannot
> imagine any testsuite which can detect that the indeterminate values
> are actually values which make sense to the implementation. In fact,
> the testsuite has to expect arbitrary values from ios_base members
> and thus cannot tell whether they are initialized or not even though
> some values may look as if they result from some form of initialization.

Okay, so you *can* initialize ios_base even though you don't have to,
but what if your implementation *requires* that ios_base not be
initialized in its constructor? Our library uses the nifty counter
trick for linkers that don't help out. We're pretty much obliged to
leave ios_base uninitialized at construction.

> The next two sentences impose restrictions on the user of the class,
> not on the implementer. These two sentences give the implementer the
> freedom to leave the ios_base members indeed indeterminate. Of course,
> the implementer can have setup ios_base properly or least in an
> destructible state (e.g. be zero initializing the callback list and
> the pword()/iword() members).

You're talking about what an implementor can do in a favorable
environment. That's not always possible.

>                               Which technique is chosen depends on
> whether the implementation accepts double construction of the
> predefined objects: if it does, it will probably not allocate any
> memory and may crash upon destruction. If the double construction is
> prevented by some approach, the ios_base constructor can actually
> properly setup the object.

True, but you haven't won much. Instead of having a class that
always causes trouble when used alone (as warned about in the C++
Standard), you now have one that works right on some systems then
crashes mysteriously when moved to another. Choose your poison.

>> : ~ios_base()
>> :
>> : 2 Effects: Destroys an object of class ios_base. Calls each
>> : registered callback pair (fn,index) (27.4.2.6) as
>> : (*fn)(erase_event,*this,index) at such time that any ios_base
>> : member function called from within fn has well defined results.
>>
>> As I said, ios_base is *obliged* to have an ineffectual constructor
>> and a nontrivial destructor.
>
> ios_base is obliged to have a non-trivial destructor. I don't think
> that the restriction about indeterminate values is an obligation to
> leave the members alone, i.e. ios_base can have a constructor which
> initializes the members but the user cannot rely on it having such
> a constructor.

You said that before, but it's simply not true in many environments.
And that was what was contemplated in the specification of ios_base.
Remember, the problem has existed since the earliest days of C++,
but is now made worse by spreading the double construction requirement
across a template class and its base class -- then exposing the base
class to the world.

>> The people who get in trouble are the
>> ones who try to make a freestanding ios_base object, in the teeth
>> of this noxious restriction.
>
> I agree wholeheartedly with this statement and I'm not sure whether
> it is helping these people if the problem is masked by an
> implementation which behaves like some users might expect that it
> behaves. There is no guarantee and producing odd behavior (like e.g.
> crashing during destruction) is a good approach to indicate that
> something is wrong.

Agreed.

>>>> But to make matters worse, Standard C++
>>>> iostreams are templatized. In the process of debugging this
>>>> enhancement, the C++ committee decided to split the old ios
>>>> class (the base of all iostreams objects) into a templatized
>>>> basic_ios<CharT> and a common non-template class ios_base.
>>>> For the nifty counter trick to work, class ios_base *must*
>>>> be properly initialized by basic_ios<CharT>(!)
>>>
>>> I disagree. 'ios_base' may initialize itself properly in a way
>>> which allows independent construction and destruction (I'm, however,
>>> not really sure whether I have realized this in the publically
>>> available version of my own implementation; after all, the standard
>>> makes no guarantees object such objects...).
>>
>> Really? See above.
>
> Yes, I think ios_base can be made well-behaved if the standard
> stream objects avoid double construction (or deal with possibly
> existing setup somehow). The restriction is upon the user who
> cannot expect ios_base to be initialized after merely default
> constructing it, although it could be properly initialized.

Uh, right. Doesn't that bring us back to square one?

>>>> Our implementation works and it conforms.
>>>
>>> Which is actually the important aspect. In fact, I think it is a
>>> service to the user to generate an error as soon as he steps into
>>> undefined behavior.
>>
>> Well, yes, but... The C and C++ Standards usually leave undefined
>> just that behavior that's hard to diagnose.
>
> This is just one kind of areas where the C and C++ standards
> use undefined behavior. Quite frequently undefined behavior is also
> used if there would be additional run-time costs to diagnose problematic
> behavior. For example, it would be trivial to setup some flag in
> ios_base constructor which indicates whether 'init()' (on basic_ios)
> was called and throw an exception when using any of ios_base members
> if this flag is not set.

Really? You mean a constructor that is supposed to do nothing at all
can do just a little something and still survive double construction?
I've thought about that idea and dismissed it a dozen times over the
past ten years or so.

>                          Of course, this approach would burdon
> ios_base operations and make it even harder to create an efficient
> IOStream implementation than is already the case. Leaving things
> undefined allows "nice" implementations diagnosing the problem while
> possibly being slower and fast implementations which assume that the
> user obeys the rules.

I don't think performance is an issue here, it's making the damn
thing work at all.

> BTW, it is not a completely odd idea to use a freestanding ios_base
> object: for example, the num_put and num_get member functions take
> ios_base objects for the formatting and possibly take additional
> arguments for the fill character (in the case of num_put) or state
> flags (in the case of num_get). This does not really make any sense
> if each ios_base object is really some 'basic_ios<cT, traits>'
> instantiation because it would be more reasonable to pass the
> 'basic_ios<cT, traits>' object in the first place and thus just one
> rather than multiple parameters.

Yes, this is a misleading spec. It's clear when you write code
that uses these facets that you're expected to slice an iostream
object to get to the ios_base. It's declared that way to show
that all the facet needs is the stuff in the ios_base base class.
But if you read these declarations as an invitation to whomp up
a freestanding ios_base object, you'll soon be brought up short.

>                                 Of course, people who were involved
> in the standardization of clause 22 know that it is riddled with
> problems but this is not that obvious to users...

Exactly. And this is the point James Kanze tried to make, I think.
Overall, iostreams presents a decent interface to the world. The
mistake lay in having the C++ Standard expose too much of the
innards to the world.

P.J. Plauger
Dinkumware, Ltd.
http://www.dinkumware.com


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: "Martin Bonner" <martinfrompi@yahoo.co.uk>
Date: 29 Jun 2005 15:00:01 GMT
Raw View
Martin Bonner wrote:
>> or are you saying that the internal design of the
>> implementation provided with Microsoft Visual C++
>> could do with improvement?

Dietmar Kuehl wrote:
> If P.J.Plauger thought that the library shipping with MSVC++ should
> be improved, he would probably just improve it (although there are
> probably rather severe constraints with respect to binary compatibility).

Yes, I knew that perfectly well :-)

Martin Bonner wrote:
>> Anyway, what is it about the specification that
>> makes it difficult to implement robustly?

"P.J. Plauger" wrote:
[a very long and detailed explanation of the problem]
Thank-you.

> I get a headache every
> time I have to revisit this code.

and having read your explanation, I can see why!  It's a weak area of
the standard (but probably not one that should be fixed).

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: Dietmar Kuehl <dietmar_kuehl@yahoo.com>
Date: Wed, 29 Jun 2005 10:59:54 CST
Raw View
Krzysztof Zelechowski wrote:
> Uzytkownik "Dietmar Kuehl" <dietmar_kuehl@yahoo.com> napisal w wiadomosci
> news:3ie6diFl4197U1@individual.net...
>> Well, technically it is possible to create an ios_base object but you
>> can't destruct it. This is what the standard says. The standard puts
>
> But I can inherit from an ios_base and destroy the inherited object.

Not according to the standard: the only way to put an 'ios_base' object
into a destructible state is by calling 'basic_ios<...>::init()' - a
member function of a class derived from 'ios_base'. The standard is
rather precise on this in a paragraph quoted by P.J.Plauger before:

  27.4.2.7 - ios_base constructors/destructors [lib.ios.base.cons]

  ios_base();

  -1- Effects: Each ios_base member has an indeterminate value after
      construction. These members must be initialized by calling
      basic_ios::init. If an ios_base object is destroyed before these
      initializations have taken place, the behavior is undefined.

(this is the C++-2005 version).

That is, you cannot destruct an 'ios_base' object unless it was initialized
by 'basic_ios<...>::init()' which in turn is only possible if the 'ios_base'
object is actually a 'basic_ios<...>'.

> I meant that these rules are a flaw in the design of the base class.  The
> compiler should generate an error if it sees a class inheriting from
> ios_base and not inheriting from basic_ios.

Well, yes, this would have been a nice idea and this can indeed be
seen as a flaw. On the other hand, the compiler does not produce an
error if you dereference a null pointer either - and this is a much
more likely scenario. Don't get me wrong: I agree with you that it
would have been better if some stuff in the IOStream hierarchy would
have been done differently and separating 'ios_base' and 'basic_ios'
is definitely an area which has a flawed design. On the other hand,
these flaws surface only rarely and, as I have pointed out in another
thread, can even be avoided if an implementation really wants to
(which I would, however, still consider to be a disservice to the
user).

> Think about the following
> scheme: make the destructor private and make friends with basic_ios so
> that I cannot inherit but basic_ios can.

I can think of many schemes how some details of the IOStreams
hierarchy could be improved but as P.J.Plauger pointed out in
this thread none of them is likely to happen anyway because IOStreams
are best left untouched before introducing new problems by
"improving" them.

> If this is not possible, my derived class should not crash in the
> destructor because the offending piece of code could be in some dark
> corner my customer does not look into until five years from now, e.g.
> until it encounters a keyword WON_HSARC in his input file.

Obviously, we have different ideas of what constitutes quality code.
In my definition, a rather high percentage of all code is covered by
automated test cases, best run under the control of some problem
detecting software like e.g. Purify. Code shall only make it into a
production system when the test cases cover at least 9x% of the code
(x >= 5; 100% of the code is desirable but not always achievable due
to error conditions which are hard to produce).

> Crashing does not help the developer
> as compiler error messages do because only the compiler sees all my code,
> the executing application need not.  Point.

I entirely agree that it is preferable to have the compiler or some
static analysis tool discover errors. You may ask Programming Research
if they support a warning in their QMTest tool if a user attempts to
derive from 'ios_base'. I'm sure that this is an easy test for them to
add... If you consider detection of illegal derivation from 'ios_base'
essential, you are welcome to champion a corresponding proposal for the
next round of standardization. However, be warned that you will have
spent a substantial amount of resources before you manage to push it
into the standard, e.g. because you will have to show up at several
committee meetings before you managed to get sufficient people convinced
that this is the right thing to do.

> Of course you can RTFM everyone, but C++ claims RTFMing and memorizing
> everything is not that crucial because the compiler can handle it itself -
> and it is an important advantage over C.  The destructor crash phenomenon
> makes this claim invalid.

I don't think that C++ made a claim that you can safely hack away and
never encounter any problem which is not detected at compile time. It
surely made the claim that it provides tools which can be used to
prevent certain kinds of errors. For whatever reason, these tools were
not applied with sufficient rigor to the IOStreams hierarchy. There
were several statements to this end in this thread already. I cannot
tell you the reasons why the IOStreams hierarchy is that riddled with
flaws as it predates my participation in the C++ committee but I know
that nobody in the current Library Working Group is too keen to make
radical changes to this area of the library. IOStreams as currently
defined work pretty well despite the many opportunities where they
could be improved if someone dared to take the blame for all problems
he introduces during the improvements.
--
<mailto:dietmar_kuehl@yahoo.com> <http://www.dietmar-kuehl.de/>
<http://www.eai-systems.com> - Efficient Artificial Intelligence

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: yecril@bluebottle.com ("Krzysztof elechowski")
Date: Sun, 26 Jun 2005 15:11:41 GMT
Raw View
The Standard says that you cannot derive from ios_base otherwise than via
basic_ios.  Is the compiler required to detect this forbidden derivation?
Microsoft Visual C++ does not detect it and my program breaks with an access
error (Microsoft's term for a bus error) in ~ios_base().
If I had designed such a confusing class and refused to make it more
foolproof, I would probably have been fired.
Christopher


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: pjp@dinkumware.com ("P.J. Plauger")
Date: Mon, 27 Jun 2005 03:03:44 GMT
Raw View
""Krzysztof    elechowski"" <yecril@bluebottle.com> wrote in message
news:d9mg2u$mr2$1@sklad.atcom.net.pl...

> The Standard says that you cannot derive from ios_base otherwise than via
> basic_ios.  Is the compiler required to detect this forbidden derivation?

No.

> Microsoft Visual C++ does not detect it and my program breaks with an
> access error (Microsoft's term for a bus error) in ~ios_base().

Sounds right.

> If I had designed such a confusing class and refused to make it more
> foolproof, I would probably have been fired.

Probably. I agree that the design sucks.

P.J. Plauger
Dinkumware, Ltd.
http://www.dinkumware.com


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: martinfrompi@yahoo.co.uk
Date: Mon, 27 Jun 2005 10:18:10 CST
Raw View
"P.J. Plauger" wrote:
> ""Krzysztof    elechowski"" <yecril@bluebottle.com> wrote in message
> news:d9mg2u$mr2$1@sklad.atcom.net.pl...
>
> > The Standard says that you cannot derive from ios_base otherwise than via
> > basic_ios.
> > [snip]
> > Microsoft Visual C++ does not detect it and my program breaks with an
> > access error (Microsoft's term for a bus error) in ~ios_base().
> >
> > If I had designed such a confusing class and refused to make it more
> > foolproof, I would probably have been fired.
>
> Probably. I agree that the design sucks.
>
> P.J. Plauger

Now then.  Are you saying that the external interface (as specified in
the Standard) is poor, or are you saying that the internal design of
the implementation provided with Microsoft Visual C++ could do with
improvement?

If you mean the former, I don't think you actually /are/ agreeing with
Krzysztof, as I think he means the latter.  Anyway, what is it about
the specification that makes it difficult to implement robustly?


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]