Topic: offsetof macro, can it be used in non-trivial classes?
Author: James Kanze <james-albert.kanze@vx.cit.alcatel.fr>
Date: 1997/01/20 Raw View
phalpern@truffle.ma.ultranet.com (Pablo Halpern) writes:
|> James Kanze <james-albert.kanze@vx.cit.alcatel.fr> wrote:
|>
|> [ as part of solution to avoid using offsetof ]
|> >
|> > class thing
|> > : private specialized_callback_1
|> > , private specialized_callback_2
|> >
|>
|> This works fine if the two classes are different, but I have a situation
|> in which both classes are the same. Since you can't inherit immediately
|> from the same class twice, [...]
You need an additional, user defined, level of inheritance.
I seem to recall reading somewhere (a long time ago) that in C, the
universal solution to any problem is to add a level of indirection
(i.e.: a pointer). In C++, I think that it is a level of inheritance.
--
James Kanze home: kanze@gabi-soft.fr +33 (0)1 39 55 85 62
office: kanze@vx.cit.alcatel.fr +33 (0)1 69 63 14 54
GABI Software, Sarl., 22 rue Jacques-Lemercier, F-78000 Versailles France
-- Conseils en informatique industrielle --
---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std-c++@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++-request@ncar.ucar.edu
]
Author: Christopher Eltschka <celtschk@physik.tu-muenchen.de>
Date: 1997/01/15 Raw View
Pablo Halpern wrote:
>
> James Kanze <james-albert.kanze@vx.cit.alcatel.fr> wrote:
>
> [ as part of solution to avoid using offsetof ]
> >
> > class thing
> > : private specialized_callback_1
> > , private specialized_callback_2
> >
>
> This works fine if the two classes are different, but I have a situation
> in which both classes are the same. Since you can't inherit immediately
> from the same class twice, I was forced to use one of the following
> solutions:
>
> class thing;
> class specialized_callback : public callback
> {
> public: specialized_callback(thing* p) : parent(p) { ... }
> private: thing* parent;
> ...
> };
>
> class thing
> {
> specialized_callback c1;
> specialized_callback c2;
> public:
> thing() : c1(this), c2(this) { ... }
> };
>
> OR
>
> class specialized_callback1 : public specialized_callback { ... };
> class specialized_callback2 : public specialized_callback { ... };
>
> class thing : private specialized_callback_1,
> private specialized_callback_2
> {
> ...
> };
>
> For my application, I chose the first option, partially because it
> avoided the forwarding constructors needed for the second option and
> seemed at the time easier to read and comment. The second solution has
> the advantage of not carrying around the extra pointer to the parent
> thing object.
>
what about the following solution:
class thing;
template<int which> class specialized_callback: public callback
{
// ... (code doesn't depend on which)
};
class thing: private specialiced_callback<1>,
private specialized_callback<2>
{
// ...
};
The trick is, that the compiler expands the template into two
different, but identic classes.
You can even give some information as template parameter,
which you would have given as constructor parameter normally,
f.ex.:
template<int event> class specialized_callback: public callback
{
specialized_callback() { register(*this,event); }
// ...
};
class thing: private specialized_callback<event1>,
private specialized_callback<event2>
{
// ...
};
[...]
---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std-c++@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++-request@ncar.ucar.edu
]
Author: phalpern@truffle.ma.ultranet.com (Pablo Halpern)
Date: 1997/01/09 Raw View
James Kanze <james-albert.kanze@vx.cit.alcatel.fr> wrote:
[ as part of solution to avoid using offsetof ]
>
> class thing
> : private specialized_callback_1
> , private specialized_callback_2
>
This works fine if the two classes are different, but I have a situation
in which both classes are the same. Since you can't inherit immediately
from the same class twice, I was forced to use one of the following
solutions:
class thing;
class specialized_callback : public callback
{
public: specialized_callback(thing* p) : parent(p) { ... }
private: thing* parent;
...
};
class thing
{
specialized_callback c1;
specialized_callback c2;
public:
thing() : c1(this), c2(this) { ... }
};
OR
class specialized_callback1 : public specialized_callback { ... };
class specialized_callback2 : public specialized_callback { ... };
class thing : private specialized_callback_1,
private specialized_callback_2
{
...
};
For my application, I chose the first option, partially because it
avoided the forwarding constructors needed for the second option and
seemed at the time easier to read and comment. The second solution has
the advantage of not carrying around the extra pointer to the parent
thing object.
The point is taken, however, that there *IS* a solution (in fact, two
solutions) that do not require offsetof() or any other non-conforming
construct.
There is still one corner case I have run into where I haven't been able
to apply the above solutions: if I don't have control over the structure
of the "thing" class (e.g. if thing is a template), then I can't use
either solution. I have run into this when using the STL map and
multimap, where I know that a value is part of a pair<> structure, and I
want to get at the other member of the pair. Although pair<> looks a lot
like a POD, it is not a POD because 1) it has non-trivial constructors
and 2) the data members may not be PODs. So I can't write a conforming
program that finds the first member of a pair given the second member.
(The reverse may be possible. If foo is an x, I think the following
would work, although the reinterpret_cast<> still makes conformance a
problem: "(static_cast<pair<x,y>*>(&foo))->second")
-------------------------------------------------------------
Pablo Halpern phalpern@truffle.ultranet.com
I am self-employed. Therefore, my opinions *do* represent
those of my employer.
---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std-c++@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++-request@ncar.ucar.edu
]
Author: Richard Krehbiel <rich@kastle.com>
Date: 1997/01/03 Raw View
stephen.clamage@Eng (Steve Clamage) writes:
> Richard Krehbiel <rich@kastle.com> writes:
>
> >stephen.clamage@Eng (Steve Clamage) writes:
>
> >> phalpern@truffle.ma.ultranet.com (Pablo Halpern) writes:
> >>
> >> >Jean-Louis Leroy <100611.1330@compuserve.com> wrote:
> >>
> >> >><< How reliable is the offsetof macro when used on a complex class
> >> >><< under MI?
> >> >>
> >> >>As the others say, it's a bad idea. In most situations, pointers
> >> to data members >>will do the job better.
> >>
> >> > int f(int *p)
> >> > {
> >> > // Problem: If we know that p is the y member of a foo object,
> >> > // get the address of the entire foo object
> >> > }
> >>
> >> But why do you want to solve that problem?
>
> >It's useful to do so in my project.
>
> >> You can't know that p points to a member of a foo (or to *which*
> >> member of a foo) without additional information.
>
> >I have the additional info I need: I wrote the code, and I know that
> >all instantiations of x are as a particular member of y. I have
> >explicitly documented such in the one header file which declares
> >classes x and y. If some other programmer comes along and makes an x
> >that's not the given member of y, then he's ignored the code's
> >documentation and established conventions, and therefore written buggy
> >code, and he'll have to fix it.
>
> In other words, the functions of interest are always supposed to
> receive a pointer to a known member of an object of a known class type.
Yep; the receiving functions do.
But the sending (er, calling) function does not. It calls a virtual
method of a base class.
> In that case, instead of passing a pointer into the middle of the
> object, pass a pointer or reference to the whole object. That way,
> you have no additional overhead, and the compiler will insure that
> inappropriate pointers don't accidently get passed to the function.
>
> Surely this is a superior solution, and works as well in C as in C++.
Here's a paraphrase of the code I'm using now. I like it because the
callback class and derivatives have no (well, the fewest possible
number of) data members. I'd like it to be standard-conforming, but I
prefer the minimum data size (I have *lots* of things with callbacks).
class callback
{
public:
virtual void invoke() = 0;
static void register(callback&, int);
static void run();
};
void callback::register(callback &cb, int event)
{
// add cb to list of callbacks indexed by event
}
void callback::run()
{
callback *cb;
for(;;)
{
// (get next event and translate into appropriate cb)
cb->invoke();
}
}
class specialized_callback_1 : public callback
{
public:
virtual void invoke();
}
class specialized_callback_2 : public callback
{
public:
virtual void invoke();
}
class thing
{
public:
thing();
friend class specialized_callback_1;
friend class specialized_callback_2;
specialized_callback_1 cb1;
specialized_callback_2 cb2;
private:
void invoke_1();
void invoke_2();
};
thing::thing()
{
callback::register(cb1, 1);
callback::register(cb2, 2);
}
// Here's when I use offsetof
#define CONTAINER(ptr,type,member) (type *) \
((char *)(ptr) - offsetof(type,member))
void specialized_callback_1::invoke()
{
thing *tp = CONTAINER(this, thing, cb1);
tp->invoke_1();
}
void specialized_callback_2::invoke()
{
thing *tp = CONTAINER(this, thing, cb2);
tp->invoke_2();
}
--
Richard Krehbiel, Kastle Systems, Arlington, VA, USA
rich@kastle.com (work) or richk@mnsinc.com (personal)
[ 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 ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: James Kanze <james-albert.kanze@vx.cit.alcatel.fr>
Date: 1997/01/06 Raw View
Richard Krehbiel <rich@kastle.com> writes:
|> Here's a paraphrase of the code I'm using now. I like it because the
|> callback class and derivatives have no (well, the fewest possible
|> number of) data members. I'd like it to be standard-conforming, but I
|> prefer the minimum data size (I have *lots* of things with callbacks).
I think that what you actually need is inheritance, possibly private.
|> class callback
|> {
|> public:
|> virtual void invoke() = 0;
|> static void register(callback&, int);
|> static void run();
|> };
|>
|> void callback::register(callback &cb, int event)
|> {
|> // add cb to list of callbacks indexed by event
|> }
|>
|> void callback::run()
|> {
|> callback *cb;
|> for(;;)
|> {
|> // (get next event and translate into appropriate cb)
|> cb->invoke();
|> }
|> }
|>
|> class specialized_callback_1 : public callback
|> {
|> public:
|> virtual void invoke();
virtual void invoke_1() = 0 ;
|> }
|>
|> class specialized_callback_2 : public callback
|> {
|> public:
|> virtual void invoke();
virtual void invoke_2() = 0 ;
|> }
|>
|> class thing
class thing
: private specialized_callback_1
, private specialized_callback_2
|> {
|> public:
|> thing();
|> friend class specialized_callback_1;
|> friend class specialized_callback_2;
|> specialized_callback_1 cb1;
|> specialized_callback_2 cb2;
Previous four lines not needed.
|> private:
|> void invoke_1();
|> void invoke_2();
|> };
|>
|> thing::thing()
|> {
|> callback::register(cb1, 1);
|> callback::register(cb2, 2);
callback::register( (specialized_callback_1*)( this ) , 1) ;
callback::register( (specialized_callback_2*)( this ) , 2) ;
(Actually, I think I'd put the registration in the constructors of the
specialized callback's.)
|> }
|>
|> // Here's when I use offsetof
|>
|> #define CONTAINER(ptr,type,member) (type *) \
|> ((char *)(ptr) - offsetof(type,member))
|>
|> void specialized_callback_1::invoke()
|> {
|> thing *tp = CONTAINER(this, thing, cb1);
|> tp->invoke_1();
Instead of the previous two lines:
invoke_1() ;
|> }
|>
|> void specialized_callback_2::invoke()
|> {
|> thing *tp = CONTAINER(this, thing, cb2);
|> tp->invoke_2();
Instead of the previous two lines:
invoke_2() ;
|> }
This is, after all, what inheritance is all about.
--
James Kanze home: kanze@gabi-soft.fr +33 (0)3 88 14 49 00
office: kanze@vx.cit.alcatel.fr +33 (0)1 69 63 14 54
GABI Software, Sarl., 8 rue des Francs Bourgeois, F-67000 Strasbourg, France
-- Conseils en informatique industrielle --
---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std-c++@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++-request@ncar.ucar.edu
]
Author: stephen.clamage@Eng (Steve Clamage)
Date: 1996/12/30 Raw View
Richard Krehbiel <rich@kastle.com> writes:
>stephen.clamage@Eng (Steve Clamage) writes:
>> phalpern@truffle.ma.ultranet.com (Pablo Halpern) writes:
>>
>> >Jean-Louis Leroy <100611.1330@compuserve.com> wrote:
>>
>> >><< How reliable is the offsetof macro when used on a complex class
>> >><< under MI?
>> >>
>> >>As the others say, it's a bad idea. In most situations, pointers
>> to data members >>will do the job better.
>>
>> > int f(int *p)
>> > {
>> > // Problem: If we know that p is the y member of a foo object,
>> > // get the address of the entire foo object
>> > }
>>
>> But why do you want to solve that problem?
>It's useful to do so in my project.
>> You can't know that p points to a member of a foo (or to *which*
>> member of a foo) without additional information.
>I have the additional info I need: I wrote the code, and I know that
>all instantiations of x are as a particular member of y. I have
>explicitly documented such in the one header file which declares
>classes x and y. If some other programmer comes along and makes an x
>that's not the given member of y, then he's ignored the code's
>documentation and established conventions, and therefore written buggy
>code, and he'll have to fix it.
In other words, the functions of interest are always supposed to
receive a pointer to a known member of an object of a known class type.
In that case, instead of passing a pointer into the middle of the
object, pass a pointer or reference to the whole object. That way,
you have no additional overhead, and the compiler will insure that
inappropriate pointers don't accidently get passed to the function.
Surely this is a superior solution, and works as well in C as in C++.
---
Steve Clamage, stephen.clamage@eng.sun.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 ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: phalpern@truffle.ma.ultranet.com (Pablo Halpern)
Date: 1996/12/31 Raw View
stephen.clamage@Eng (Steve Clamage) wrote:
>phalpern@truffle.ma.ultranet.com (Pablo Halpern) writes:
>
>> int f(int *p)
>> {
>> // Problem: If we know that p is the y member of a foo object,
>> // get the address of the entire foo object
>> }
>
>But why do you want to solve that problem?
The problem is with generic programming. Suppose for efficiency reasons
we want certain data structures to belong to multiple invasive linked
lists. The link list mechanism is generic and need be implemented only
once. The problem is that once a node within a list is identified, it is
necessary to get the object in which that node is nested:
struct invasive_link { generic_link *next; };
struct invasive_list
{
invasive_link *head, *tail;
// Functions that generically insert, remove, and traverse the list
// ...
};
// Print all of the objects in a list
print_list(invasive_list lst, void (*print_link)(invasive_link*))
{
invasive_link *node = lst.head;
while (node != NULL)
{
print_link(node);
node = node->next;
}
}
// Now, we have a bunch of invasive lists, used to categorize objects
invasive_list black_objects, white_objects, red_objects;
invasive_list big_objects, small_objects;
class object
{
// Member of more than one invasive list
invasive_link my_color_link;
invasive_link my_size_link;
//...
static void print_color_link(invasive_link* node);
static void print_size_link(invasive_link* node);
};
void object::print_size_link(invasive_link* node)
{
object* op = HELP! Can't get object from node!
cout << *op;
}
On the line the says HELP, we have all of the information we need to get
the object from the node (we know it's of type object, we know that the
*node is my_size_link within the object), but no way to go backwards
from the node to the object (except offsetof).
The above code is, of course, not type-safe. But type safety can be
provided easily by using templates. The above code is efficient: each
node requires only one memory allocation operation, instead of one for
the list node and one for the object itself. We could "improve" the
design by using non-invasive lists or duplicating the list management
code (using templates) but at the cost of either memory or run-time
performance.
Hmm... Now that I think about it, there is a solution to the above
problem:
struct color_link : public invasive_link { };
struct size_link : public invasive_link { };
class object : private color_link, private size_link { ... };
void object::print_size_link(invasive_link* node)
{
object* op = static_cast<object*>(static_cast<size_link*>(node));
cout << *op;
}
>
>The "offset" of a member isn't even a well-defined concept when
>virtual base classes are involved, so trying to use it for
>general C++ classes is doomed from the start. Instead of trying
>to patch up the idea of "offset" and further constraining the
>implementation of C++ classes, the C++ Committee chose to keep
>the definition of "offset" only for C-like classes, and to use
>pointer-to-member as a reliable and type-safe construct for
>general C++ classes.
I'm sure a reverse-pointer-to-member could be described that works with
virtual inheritance (using RTTI). However, I wasn't proposing a
"solution" to the problem, only that there are limits to
pointer-to-members which were not there for offsetof(). In the absence
of virtual inheritance, I don't know why offsetof couldn't work for
non-POD classes anyway.
-------------------------------------------------------------
Pablo Halpern phalpern@truffle.ultranet.com
I am self-employed. Therefore, my opinions *do* represent
those of my employer.
[ 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 ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: Jean-Louis Leroy <100611.1330@compuserve.com>
Date: 1996/12/23 Raw View
<< How reliable is the offsetof macro when used on a complex class under MI? >>
As the others say, it's a bad idea. In most situations, pointers to data members
will do the job better.
Jean-Louis
http://ourworld.compuserve.com/homepages/jl_leroy/
---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std-c++@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++-request@ncar.ucar.edu
]
Author: phalpern@truffle.ma.ultranet.com (Pablo Halpern)
Date: 1996/12/27 Raw View
Jean-Louis Leroy <100611.1330@compuserve.com> wrote:
><< How reliable is the offsetof macro when used on a complex class under MI? >>
>
>As the others say, it's a bad idea. In most situations, pointers to data members
>will do the job better.
The problem with pointers to data members is that they only work one
way. As far as I know, there is no portable solution to the following
problem:
// Assume bar is not a POD, so foo is not a POD
struct foo : public bar { int x, y, z; };
int f(int *p)
{
// Problem: If we know that p is the y member of a foo object,
// get the address of the entire foo object
}
The above problem would be solvable if &foo::y could be applied
backwards. It would also be solvable using offsetof() if foo were a POD.
Fortunately, there are very few situations like this that couldn't be
fixed by using a better design.
-------------------------------------------------------------
Pablo Halpern phalpern@truffle.ultranet.com
I am self-employed. Therefore, my opinions *do* represent
those of my employer.
[ 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 ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: Richard Krehbiel <rich@kastle.com>
Date: 1996/12/30 Raw View
stephen.clamage@Eng (Steve Clamage) writes:
> phalpern@truffle.ma.ultranet.com (Pablo Halpern) writes:
>
> >Jean-Louis Leroy <100611.1330@compuserve.com> wrote:
>
> >><< How reliable is the offsetof macro when used on a complex class
> >><< under MI?
> >>
> >>As the others say, it's a bad idea. In most situations, pointers
> to data members >>will do the job better.
>
> > int f(int *p)
> > {
> > // Problem: If we know that p is the y member of a foo object,
> > // get the address of the entire foo object
> > }
>
> But why do you want to solve that problem?
It's useful to do so in my project.
> You can't know that p points to a member of a foo (or to *which*
> member of a foo) without additional information.
I have the additional info I need: I wrote the code, and I know that
all instantiations of x are as a particular member of y. I have
explicitly documented such in the one header file which declares
classes x and y. If some other programmer comes along and makes an x
that's not the given member of y, then he's ignored the code's
documentation and established conventions, and therefore written buggy
code, and he'll have to fix it.
> The idea of
> pointer-to-member encapsulates that fact. Why not pass around
> a pointer or reference to the containing object if you care about
> the object itself?
Why make objects bigger than they need to be? If (big if) a class can
know it's always this member of that class then (theoretically at
least) storing a containing class pointer is needless. (The new ANSI
offsetof rules unfortunately make this useless knowledge.)
My own needs were for event dispatching. I have a base class which
represents a generic registered callback. I subclass this with
specializations which each know they're a particular member of another
particular class; these specializations use offsetof to find their
containing class and invoke the right callback method of their
container.
> The "offset" of a member isn't even a well-defined concept when
> virtual base classes are involved,
Sigh. I know. My project was already installed by the time ANSI got
to C++ and decided my use of offsetof was not supportable. I'll
probably have to fix it.
--
Richard Krehbiel, Kastle Systems, Arlington, VA, USA
rich@kastle.com (work) or richk@mnsinc.com (personal)
[ 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 ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: "MC" <contem00@cam.org>
Date: 1996/12/17 Raw View
How reliable is the offsetof macro when used on a complex class under MI?
---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std-c++@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++-request@ncar.ucar.edu
]
Author: James Kanze <james-albert.kanze@vx.cit.alcatel.fr>
Date: 1996/12/19 Raw View
"MC" <contem00@cam.org> writes:
|> How reliable is the offsetof macro when used on a complex class under MI?
According to the (draft) standard, it is undefined in such cases.
--
James Kanze home: kanze@gabi-soft.fr +33 (0)3 88 14 49 00
office: kanze@vx.cit.alcatel.fr +33 (0)1 69 63 14 54
GABI Software, Sarl., 8 rue des Francs Bourgeois, F-67000 Strasbourg, France
-- Conseils en informatique industrielle --
---
[ 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 ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]