Garrett D'Amore wrote:
> Reading the Apache C++ sources (which I didn't want to do, but here you
> go), I ran into the following example snippet in the include directory
> (the include file is <streambuf>):
>
> template<class _CharT, class _Traits>
> inline typename basic_streambuf<_CharT, _Traits>::int_type
> basic_streambuf<_CharT, _Traits>::
> sputbackc (char_type __c)
> {
> _RWSTD_ASSERT (_C_is_valid ());
>
> if (_C_putback_avail () && traits_type::eq (*(gptr () - 1), __c))
> return traits_type::to_int_type (*--_C_gptr);
>
> return pbackfail (traits_type::to_int_type (__c));
> }
>
>
> In the above example, the inline function has in it bits which appear to
> me to be implementation details (_C_putback_avail() is protected for
> example -- I'm not sure whether the *standard* specifies that it exist
> or not. _C_gptr is certainly a private member of the class, and I
> "presume" that the standard doesn't specify it, though I've not checked
> the standard since I don't have a copy of it handy.)
>
> The concern here is what happens if those portions of the implementation
> need to change? (What happens if _C_gptr moves in the class, or the
> implementation needs to change in a way that _C_gptr doesn't exist at
> all, or changes type? Bad things, I suspect.)
These changes cannot be made. Not for the duration of Major Release 4.
This is what the Standard says about this particular member function:
27.5.2.2.4 Putback
int_type sputbackc(char_type c);
1 Returns: If the input sequence putback position is not available, or if
traits::eq(c, gptr()[-1]) is false, returns
pbackfail(traits::to_int_type(c)). Otherwise, decrements the next
pointer for the input sequence and returns
traits::to_int_type (*gptr()).
The function _C_putback_avail() is private to this implementation of the
Library
(in the Apache Library's case all functions, or data members whose names begin
with "_C" are private to the implementation). The pointer _C_gptr (or a
functional equivalent thereof) is mandated by the Standard. This naming
convention is a warning to the developer of the application consuming the
interfaces exposed by <streambuf>: Do Not Use These Functions Directly Under
Any
Circumstances Whatsoever. Also, the fact that is is protected imposes
restrictions on access.
The important aspect is that the gptr() (which Apache calls _C_gptr) is
explicitly mentioned in the Standard.
If you compare version 4.2.1 of this Standard header file with the same header
file from versions 4.2.0, or 4.1.3, you will see that the changes made to this
header file do not break binary compatibility.
> Will the implementation take care to preserve the *existing* functions
> so that the above inline (which will now have been compiled into various
> applications) will continue to function, regardless of what other
> changes may be necessary for the class?
The implementation *must* preserve those functions and data members (or
functional equivalents thereof) which are mandated by the Standard. These
cannot
be changed in any way -- they are immutable.
New, non-virtual member functions could be added to this class (non-virtual
functions do not affect the size or the layout of the object, or of the
__vtbl),
provided that their names begins with "_C", and are therefore labeled as
private
to the implementation. Static member functions could be safely added (they
don't
even have an implicit "this" pointer at all, and they do not affect the size or
the layout of the object), subject to the same naming convention constraints.
New virtual member functions, and/or new data members cannot be added, under
any
circumstances whatsoever. But such additions should not be even considered in
the first place -- this version of the Standard is frozen. Adding a virtual
function to this class will first and foremost violate the interfaces mandated
by the Standard.
What could change, with less restrictions, are the private implementation
details which can be found in the $(top_srcdir)/src directory, and whose names
begin with "__rw".
> I apologize for my apparently childish C++ example earlier -- as I said,
> I don't work in the language very often. But I *think* I've found a
> real example from Apache C++ that suffers the same problem. Do you
> disagree?
I do not see this example as a reason for binary compatibility breakage
concern,
provided that the rules for preserving binary compatibility are observed (which
I have no reason to believe that they will not be). Everything i've said here i
can guarantee that is is also very well known by the Apache/RogueWave
developers
as well -- most likely much more.
No apologies necessary. There are design circumstances in which the developer
of
the library interface simply knows that the class layout cannot change, ever.
For those circumstances, freezing the object layout is appropriate.
KDE, and QT, intentionally do not use this approach. For all the library
classes
which expose public interfaces available to external consumers, the private
implementation details are hidden by means of the Bridge or Facade Design
Patterns (pImpl pattern). The actual implementation of the header file only
exposes member functions, and the only data member is an opaque pointer to the
private implementation. The actual private implementation is done in the *.cpp
translation unit.
--Stefan
--
Stefan Teleman
Sun Microsystems, Inc.
Stefan.Teleman at Sun.COM