Topic: Anonymous union rules against constructors & assignment
Author: crosbie@digitalproductions.co.uk ("Crosbie Fitch")
Date: Fri, 9 Jun 2006 12:42:25 GMT Raw View
<kuyper@wizard.net> wrote in message
news:1149802608.137202.98340@j55g2000cwa.googlegroups.com...
> And as such, unions really has no place in C++ - it's a primitive
> throwback that makes type safety excessively difficult to achieve. It's
> there mainly for backwards compatibility with C.
I think half the problem is the strange attempt to make an inherently
type-subversive tool, be type safe.
The whole point of an anonymous union is to alias types for a particular
storage area and provide extremely concise and neat syntax for accessing
them.
Perhaps the syntax for a union looks too similar to that for a struct, so
people treat it as a dangerous struct that has to have its sharp edges
blunted, instead of a powerful blade that needs extreme care when used -
generally as rarely as the cast operators.
A compromise that could be more palatable to those shy of having sharp
knives in the toolbox could be to say that an anonymous union can contain
POD members or empty non-POD members (no vtable either). However, I daresay
you'll tell me that even empty non-POD members are permitted to have a
secret data payload that would be trashed within a union.
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: crosbie@digitalproductions.co.uk ("Crosbie Fitch")
Date: Fri, 9 Jun 2006 12:43:00 GMT Raw View
<johnchx2@yahoo.com> wrote in message
news:1149696094.781890.80420@f6g2000cwb.googlegroups.com...
>> class A
>> {
>> A& CustomAssignCopy(const A& a) { /*copy*/ return *this; }
>> public:
>> A& operator=(const A& a) const { return
>> const_cast<A*>(this)->CustomAssignCopy(a); }
>> };
>>
>> union U
>> {
>> A a;
>> };
>>
>>
> I can't see why that would be legal, nor does either g++ (3.4.2) or
> comeau online. What compiler accepts this?
OOOPS. Sorry. Insufficiently accurate recall of the hack. Many apologies!
This should be a compilable version of the hack.
#include <iostream>
struct Apple
{
int a,b;
};
class A: public Apple
{
A& CustomAssignCopy(int i) { a=i; return *this; }
public:
operator int() const { return a; }
A& operator=(int i) const { return
const_cast<A*>(this)->CustomAssignCopy(i); }
};
class B: public Apple
{
B& CustomAssignCopy(int i) { b=i; return *this; }
public:
operator int() const { return b; }
B& operator=(int i) const { return
const_cast<B*>(this)->CustomAssignCopy(i); }
};
class UA
{
public:
union
{ Apple apple;
const A a; // const to prevent default assign-copy being called
const B b; // const to prevent default assign-copy being called
};
UA() { apple.a=0; apple.b=0; }
UA(int a,int b) { apple.a=a; apple.b=b; }
UA& operator=(const UA& ua) { apple=ua.apple; return *this; }
};
int _tmain(int argc, _TCHAR* argv[])
{ UA u(123,456),v(7,8);
// u=v;
printf("u.a=%d,u.b=%d\n",(int)u.a,(int)u.b);
u.a=v.a;
printf("u.a=%d,u.b=%d\n",(int)u.a,(int)u.b);
printf("sizeof(UA)=%d, sizeof(Apple)=%d\n",sizeof(UA),sizeof(Apple));
return 0;
}
By making the union member const, the default assignment-copy operator is
never selected because it requires a non-const lvalue, therefore my
assignment from int is selected because it can work with a const
lvalue.
If it wasn't const, the default assign copy operator would have been called
which would copy a AND b - even though that's not what I want, and I'm not
allowed to override it.
NOW I can get all indignant! :)
And I'm led to believe the above code is perfectly safe UNTIL A or B is
permitted to have a non-default assign-copy operator.
> I'm curious to know more about the specific problem at hand. I gather
> that you want to include in a union a type which is otherwise POD but
> which requires a custom copy assignment operator. I'm having trouble
> envisioning what that would look like. Can you elaborate?
Sure, look for the anonymous union below and the members I'm having to make
const in order to prevent the default assign-copy operator being called
(which some nanny has decided that I shouldn't be permitted to override
because I might hurt myself).
[REPOSTING less code - I suspect may have been rejected by moderators due to
too much contextual code]
template <class VECTOR_REPRESENTATION, bool IS_ROW, class
VECTOR_TRAITS=vector_traits<VECTOR_REPRESENTATION> >
class vector_base: protected VECTOR_TRAITS // General homogenous 3D vector
{
public:
explicit vector_base(const VECTOR_REPRESENTATION& v):representation(v) { }
public:
// explicit vector_base(const VECTOR_TRANSPOSE& v):representation(v) { }
// static vector_base construct(const VECTOR_REPRESENTATION& v) { return
v; }
typedef typename VECTOR_TRAITS::vector_representation
vector_representation;
typedef typename VECTOR_TRAITS::precision precision; // Used for internal
passing
typedef typename VECTOR_TRAITS::scalar scalar; // Used for external
arguments
enum { default_is_row=VECTOR_TRAITS::default_is_row,
default_is_column=VECTOR_TRAITS::default_is_column };
typedef typename VECTOR_TRAITS::vector_default vector_default;
typedef typename VECTOR_TRAITS::normal_default normal_default;
typedef typename VECTOR_TRAITS::vector_row vector_row;
typedef typename VECTOR_TRAITS::vector_column vector_column;
enum { is_row=IS_ROW,is_column=!IS_ROW,is_vector=(IS_ROW==default_is_row),
is_normal=(IS_ROW==default_is_column) };
typedef typename vector_base<VECTOR_REPRESENTATION,IS_ROW,VECTOR_TRAITS>
vector_this;
typedef typename vector_base<VECTOR_REPRESENTATION,!IS_ROW,VECTOR_TRAITS>
vector_transpose;
public:
union
{ VECTOR_REPRESENTATION representation;
// CONVENIENT PROPERTIES - read & write
const vector_property<property_indexed_element<vector_representation,0> >
x;
const vector_property<property_indexed_element<vector_representation,1> >
y;
const vector_property<property_indexed_element<vector_representation,2> >
z;
const vector_property<property_indexed_element<vector_representation,3> >
w;
vector_property<property_cast<vector_representation,vector_transpose> > t;
// Tranpose
vector_property<property_cast<vector_representation,normal_default> > n;
// As a normal
vector_property<property_cast<vector_representation,vector_default> > v;
// As a vector
vector_property<property_cast<vector_representation,vector_row> > r; //
As a row vector
vector_property<property_cast<vector_representation,vector_column> > c;
// As a column vector
const vector_property<property_magnitude<vector_representation> > m; //
Magnitude
const vector_property<property_magnitude_squared<vector_representation> >
s; // Magnitude squared
const
vector_property<property_direction<vector_representation,vector_this> > d;
// Direction (returns this normalised, or assigns a new direction without
affecting magnitude)
const vector_property<property_alpha<vector_representation> > a; // Polar
rotation about z
const vector_property<property_beta<vector_representation> > b; // Polar
elevation into z
};
// Constructors
vector_base() { } // Potentially controversial non-initialisation
vector_base(const vector_base& v) { representation.assign(v); }
vector_base& operator=(const vector_base& v) { representation.assign(v);
return *this; }
vector_base(double dx,double dy,double dz,double dw=0.0)
{ representation.initialise((scalar)dx,(scalar)dy,(scalar)dz,(scalar)dw);
}
static vector_base polar(scalar alpha,scalar beta,scalar magnitude,scalar
dw=0.0)
{ // todo
}
// Conversions
operator bool() const { return !representation.is_null(); }
operator const VECTOR_REPRESENTATION&() const { return representation; }
// operator unit_vector() const;// Convert
//operator unit_normal() const; // Convert
// Functions
bool operator!() const { return representation.is_null(); }
vector_base operator~() const { return representation.direction(); } //
Returns the direction of the position vector or unit vector if w=0
vector_base operator-() const { return representation.negation(); }
// Assignments
// Modifications
vector_base& operator*=(scalar s) { representation.scale(s); return
*this; }
vector_base& operator/=(scalar s) { representation.scale(scalar(1)/s);
return *this; }
vector_base& operator+=(const vector_base& v) { representation.add(v);
return *this; }
vector_base& operator-=(const vector_base& v) { representation.subtract(v);
return *this; }
};
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: kuyper@wizard.net
Date: Fri, 9 Jun 2006 07:58:50 CST Raw View
"Crosbie Fitch" wrote:
> <kuyper@wizard.net> wrote in message
> news:1149802608.137202.98340@j55g2000cwa.googlegroups.com...
> > And as such, unions really has no place in C++ - it's a primitive
> > throwback that makes type safety excessively difficult to achieve. It's
> > there mainly for backwards compatibility with C.
>
> I think half the problem is the strange attempt to make an inherently
> type-subversive tool, be type safe.
No such attempt is being made. An attempt is being provide support for
unions that is minimally adequate to allow legacy C code to compile and
function properly as C++ code, while discouraging any other use of
unions - because they are, as you point out, inherently
type-subversive.
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: James Dennett <jdennett@acm.org>
Date: Fri, 9 Jun 2006 08:07:46 CST Raw View
Crosbie Fitch wrote:
> <kuyper@wizard.net> wrote in message
> news:1149802608.137202.98340@j55g2000cwa.googlegroups.com...
>> And as such, unions really has no place in C++ - it's a primitive
>> throwback that makes type safety excessively difficult to achieve. It's
>> there mainly for backwards compatibility with C.
>
> I think half the problem is the strange attempt to make an inherently
> type-subversive tool, be type safe.
>
> The whole point of an anonymous union is to alias types for a particular
> storage area and provide extremely concise and neat syntax for accessing
> them.
No, I don't believe that's the case. You may see a union
(specifically an anonymous union) as being just about
allowing aliasing, but the C++ standard does not, and
indeed has many rules to say that this is not what a
union is for. Your argument appears to assume its own
conclusion (that unions should be for overlaying storage).
It's already been pointed out that announcing that the
rationale behind unions is misguided is not likely to be
an effective way to persuade the committee to do anything,
as the decisions have been made on the basis of careful
consideration.
You think unions are there to subvert the type system;
those who designed their rules think that unions are not
about subverting the type system, but are a (rather
limited) way to express a notion that only one object
is active at a time out of a set of possible objects,
*without* breaking guarantees of the C++ object model
on construction, assignment etc.
The rules for union members are intended to ensure that
using unions does not stamp on C++'s object model. It's
not a bad thing that you have to do more work to subvert
the C++ object model; it's not what unions are for.
-- James
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: crosbie@digitalproductions.co.uk ("Crosbie Fitch")
Date: Fri, 9 Jun 2006 14:41:58 GMT Raw View
<kuyper@wizard.net> wrote in message
news:1149857673.374496.65210@i40g2000cwc.googlegroups.com...
> No such attempt is being made. An attempt is being provide support for
> unions that is minimally adequate to allow legacy C code to compile and
> function properly as C++ code, while discouraging any other use of
> unions - because they are, as you point out, inherently
> type-subversive.
Ok, so why was the anonymous union added to the C++ language if not in
recognition of the value of a type-subversive tool?
Why were methods added to named unions - which is tantamount to adding wings
to a pig?
By all means restrict named unions as C style unions restricted to POD
members with no methods, but let the anonymous union realise its full
potential as a type subversive, reintepret_casting memory overlay.
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: crosbie@digitalproductions.co.uk ("Crosbie Fitch")
Date: Fri, 9 Jun 2006 14:42:39 GMT Raw View
I've found a hack that works in MS VS 2005.
Unfortunately, it's uses non-standard C++ (anonymous struct), but hey, it
works.
This hack enables unlimited pseudo members occupying zero overhead.
#include <iostream>
struct Data
{ int data[2];
void Init(int a=0,int b=0) { data[0]=a; data[1]=b; }
static Data New(int a=0,int b=0) { Data d; d.Init(a,b); return d; }
};
template <int ITEM>
class Item: Data
{
public:
operator int() const { return data[ITEM]; }
Item& operator=(int i) { data[ITEM]=i; return *this; }
Item& operator=(const Item& item) { data[ITEM]=item.data[ITEM]; return
*this; }
};
class Twin
{
public:
union
{ Data twin;
struct { Item<0> d0; };
struct { Item<1> d1; };
};
Twin():twin(Data::New()) { }
Twin(int a,int b):twin(Data::New(a,b)) { }
Twin& operator=(const Twin& t) { twin=t.twin; return *this; }
};
int main()
{ Twin u(123,456),v(7,8);
printf("u.d0=%d, u.d1=%d\n",(int)u.d0,(int)u.d1);
u.d0=v.d0; // u.d1 should be left unaffected
printf("u.d0=%d, u.d1=%d\n",(int)u.d0,(int)u.d1);
printf("sizeof(Twin)=%d, sizeof(Data)=%d\n",sizeof(Twin),sizeof(Data));
return 0;
}
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: kuyper@wizard.net
Date: Fri, 9 Jun 2006 23:04:23 CST Raw View
"Crosbie Fitch" wrote:
.
> Ok, so why was the anonymous union added to the C++ language if not in
> recognition of the value of a type-subversive tool?
>
> Why were methods added to named unions - which is tantamount to adding wings
> to a pig?
>
> By all means restrict named unions as C style unions restricted to POD
> members with no methods, but let the anonymous union realise its full
> potential as a type subversive, reintepret_casting memory overlay.
I get skeptical when people say they need to subvert the design of a
system - there are exceptions, but what it often means is they want to
achieve something the system was deliberately and correctly designed to
prevent them from doing.
You've given an example of a horrendous work-around that you claim that
the current rules force you to use. However, the work-around was so
horrendous that I couldn't figure out what real problem it was intended
to solve, much less evaluate whether it was in fact the best available
way of solving that problem with the current rules.
Let's assume for the moment that your suggestion were implemented.
Could you give us an example of code that would use this feature to
solve a real problem, where use of this feature would clearly allow for
a much better solution than the best alternative that doesn't use that
feature? For the sake of answering this question, assume that the tr1::
features that others have mentioned in this thread are all available as
alternatives. I'm not looking for an example that merely demonstrates
the feature - I'm looking for an example that demonstrates the need for
the feature.
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: crosbie@digitalproductions.co.uk ("Crosbie Fitch")
Date: Sun, 11 Jun 2006 02:58:32 GMT Raw View
<kuyper@wizard.net> wrote in message
news:1149874736.499218.21710@c74g2000cwc.googlegroups.com...
> I get skeptical when people say they need to subvert the design of a
> system - there are exceptions, but what it often means is they want to
> achieve something the system was deliberately and correctly designed to
> prevent them from doing.
An anonymous union is type subversive. It wasn't me that actually added it
to C++ when it didn't exist in C.
I have not said I need to subvert the design of any system. I simply require
the anonymous union to work as it was designed to.
The folk who invented PODness have facilitated this subsequent notion to
warp C++ away from its laissez faire agnosticism into a perverted duality of
object represention. It is they who hobble the type subversive tools
originally in the toolset in order to support arbitrary non-POD
representation (does even MS managed C++/.NET actually take advantage of
this liberty the standards committee has engineered for it?).
We now have two kinds of primitive objects:
1) Those with any number of simple data types and any number of methods,
assignment and other operators,
2) and those that don't define an assign-copy operator - though they can
still define assign-copy-base, assign-copy-derivative, and any other
assignment operator they fancy.
Supposedly, this is so the compiler can decide to change the representation
of objects with a non-default assign-copy operator into anything it fancies
whether a single word or twice or more the data payload.
If an object's representation can change so drastically depending upon the
presence of a single operator, and if that object's ability to be used
elsewhere is dependent on this, then that is an example of the horrendous,
especially if the programmer is excluded from the decision. And it's either
paternalistic or a kludge on the part of the C++ standards committee.
Perhaps what we really need, given that obviously any compiler CAN represent
such objects both ways, is a representational specifier in the class
definition, e.g.
A pseudo class called pod, that when derived from always creates objects
that will be represented by the compiler as for POD objects and able to be
treated in the same way (even though they may be smart pointers or linked
list elements) which may prevent some things such as virtual members and
multiple inheritance, but not constructors/assign-copy/destructors, or
appearance as union members - given the compiler cannot implement them as
non-POD.
It's time 'pod' entered the language rather than being the C++ standards
committee's dirty little secret.
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: nevin@eviloverlord.com ("Nevin \":-]\" Liber")
Date: Tue, 13 Jun 2006 05:24:30 GMT Raw View
In article <fLGdnbqS3NJxmBXZRVny1g@bt.com>,
"Crosbie Fitch" <crosbie@digitalproductions.co.uk> wrote:
> I'm saying that a union should not attempt to construct its members.
> If I have 10 members of a union, are you really suggesting to me that each
> default constructor is called that coincidentally does nothing?
I'm far more concerned when the types have non-trivial destructors.
Personally, I'd love to see the rules for POD-ness relaxed to any type
that has trivial destruction (where the destructor is empty, and the
destructors of all of its elements are also trivial). While
constructors are used mainly to set up class invariants, they are
sometimes used as a convenient way to initialize value classes
(especially when used in conjunction with templates). [To be fair, I
haven't looked to see if anyone has proposed such a thing.]
Alignment issues notwithstanding, wouldn't the following code get you
the behavior that you want (where C is the "union" of A and B):
struct A
{
int i;
};
struct B
{
char c;
};
struct C
{
union
{
char aMemory[sizeof(A)];
char bMemory[sizeof(B)];
};
A& a;
B& b;
C() : a(*reinterpret_cast<A*>(aMemory))
, b(*reinterpret_cast<B*>(bMemory))
{}
};
>
> >> Until unions are permitted to have user defined methods like classes, the
> >> defaults have to be the C defaults, i.e. no construction/destruction and
> >> bitwise assign-copy (of the memory - not the members).
> >
> > That's what the POD rules are for. I thought that you wanted that
> > restriction removed?
>
> I'm distinguishing between the union's constructor and the constructors of
> the union's members.
> The union's constructor should by default do nothing - which is currently
> misportrayed as actually calling each of its members default constructors
> which coincidentally do nothing.
>
> > For non-POD types, C++ has abolished every guarantee that makes it
> > useful to view it through a union. In particular, the fact that
> > memcpy() is only required to work on POD types, and that sizeof() is
> > only required to produce meaningful results on POD types, allows an
> > implementation where the memory in which a non-POD object is stored is
> > non-contiguous.
>
> So, you're saying that if I have
> struct S
> {
> int i;
> S& operator=(const S& s) { i=s.i; }
> };
>
> that even if I was allowed to put it in a union, there's no way the compiler
> can compute the size of S or allocate a large enough union to contain S?
> That is one lousy compiler.
>
> How about this one:
>
> template <class POD>
> struct Empty: POD
> {
> Empty& operator=(const Empty& e) { PODMethod(e); }
> };
>
> If the other member of the union is an instance of POD, why can't Empty
> (which introduces the dreaded non-pod assignment and no additional members)
> call a POD safe method on its POD base?
>
> > I'm talking about classes with non-trivial assignment copy operators,
> > where copying them bit-wise could be a whole lot more dangerous than
> > that. It's precisely because memcpy() won't do the right thing that the
> > class has a non-trivial assignment copy operator.
>
> Let's get this straight: I agree that memcpy() is not guaranteed to copy any
> non-POD object.
> I'm saying that irrespective of that, that's no reason to prohibit a non-POD
> member within an anonymous union. Rather than prohibit it, simply say that
> memcpying an anonymous union containing a non-POD member is undefined.
> Moreover, you can say that a class that contains an anonymous union with a
> non-POD member will not have a default assign-copy operator or copy
> constructor (or default constructor or destructor).
>
> > However, your wording implied that someone has actually promoted that
> > delusion. I was just asking you who that was.
>
> I inferred it from the restrictions governing unions and their members.
>
>
> ---
> [ 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.comeaucomputing.com/csc/faq.html ]
--
Nevin ":-)" Liber <mailto:nevin@eviloverlord.com> (773) 961-1620
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: crosbie@digitalproductions.co.uk ("Crosbie Fitch")
Date: Tue, 13 Jun 2006 14:28:00 GMT Raw View
""Nevin ":-]" Liber"" <nevin@eviloverlord.com> wrote in message
news:nevin-95FA55.00133213062006@news.isp.giganews.com...
> I'm far more concerned when the types have non-trivial destructors.
It would be nice to permit non-default construction and destruction for
anonymous union members too, but they can effectively be overridden by the
enclosing class.
It's the default assignment that MUST be exposed, cannot be overridden
directly nor indirectly, and can only be prevented from selection by making
the member const (and then you can only have one such member).
> Alignment issues notwithstanding, wouldn't the following code get you
> the behavior that you want (where C is the "union" of A and B):
Yes it would get the right behaviour, but not the right payload. Your refs
add overhead.
Let's pretend we have a vast volume of legacy code with a class that exposed
public data members, and yet we need to change the behaviour of the class
without either changing its size or shape. Let's say it's a struct with two
floats, and they can't be touched, but we need to intercept them being read
or written. We could make the floats private, and expose the two original
member variable names as proxy objects exposed as empty members within an
anonymous union also containing the floats. We can trap all operations on
those members EXCEPT assign-copy. We can trap assign from float (c.x=1.f),
and cast to float (f=c.x), and assign from different member (c.x=d.y), but
not assign member from same member (c.x=d.x). All because of some tiny
clause somewhere for the benefit of theoretically possible compiler writers
who'd may require the option to completely change the memory representation
of objects with a non-default assign-copy operator. And now the only way out
of this mess is to introduce a mechanism for the programmer to say "Not for
this class you don't!" Which may take a while...
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: fgothamNO@SPAM.com (Frederick Gotham)
Date: Tue, 13 Jun 2006 15:24:17 GMT Raw View
"Nevin ":-]" Liber" posted:
> union
> {
> char aMemory[sizeof(A)];
> char bMemory[sizeof(B)];
> };
Good idea, but you're not guaranteed correct alignment. You'd need
something to the effect of:
union {
storage_for<A> aMemory;
storage_for<B> bMemory;
};
I've heard boost has something like this. I was very curious as to how it
worked, but when I went through the code it was rife with macros... very
hard to figure out.
--
Frederick Gotham
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: johnchx2@yahoo.com
Date: Tue, 13 Jun 2006 11:46:50 CST Raw View
"Crosbie Fitch" wrote:
> Yes it would get the right behaviour, but not the right payload. Your refs
> add overhead.
>
> Let's pretend we have a vast volume of legacy code with a class that exposed
> public data members, and yet we need to change the behaviour of the class
> without either changing its size or shape. Let's say it's a struct with two
> floats, and they can't be touched, but we need to intercept them being read
> or written. We could make the floats private, and expose the two original
> member variable names as proxy objects exposed as empty members within an
> anonymous union also containing the floats. We can trap all operations on
> those members EXCEPT assign-copy. We can trap assign from float (c.x=1.f),
> and cast to float (f=c.x), and assign from different member (c.x=d.y), but
> not assign member from same member (c.x=d.x).
So the feature you actually need is zero-size reference members, and
the only reason unions got into the mix was that you thought you could
use them to emulate the desired feature.
Instead of focusing on the obstacles to the rather roundabout
implementation technique, why not just propose the needed feature
itself? Personally, I see zero-size member references as *much* more
generally useful than any possible tweak to the rules governing
anonymous unions. Plus -- added benefit -- there's no need to declare
war on the basic C++ object model.
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: crosbie@digitalproductions.co.uk ("Crosbie Fitch")
Date: Tue, 13 Jun 2006 20:23:43 GMT Raw View
<johnchx2@yahoo.com> wrote in message
news:1150214774.884518.156620@g10g2000cwb.googlegroups.com...
> Instead of focusing on the obstacles to the rather roundabout
> implementation technique, why not just propose the needed feature
> itself? Personally, I see zero-size member references as *much* more
> generally useful than any possible tweak to the rules governing
> anonymous unions. Plus -- added benefit -- there's no need to declare
> war on the basic C++ object model.
I wouldn't describe it as declaring war. Rather, pointing out an incredibly
irritating kludge/paternalism.
I still reckon some GOTW out there might be able to figure out a cunning
hack that prevents selection of the default assign-copy operator (or even a
compile-time error) when the a.x=b.x assignment is encountered. Perhaps a
static member x in a base class - some kind of type mismatch that only
occurs just before selection of default assign-copy? :-}
Another hypothetical extension that might get around it would be:
X& ::operator=(X&, const Y&)
I guess the only salvation available today is C++/CLI :-/
Perhaps there'll be back propogation of 'property' from that into the C++
standard?
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: "Greg Herlihy" <greghe@pacbell.net>
Date: Tue, 13 Jun 2006 16:20:23 CST Raw View
johnchx2@yahoo.com wrote:
> "Crosbie Fitch" wrote:
>
> > Yes it would get the right behaviour, but not the right payload. Your refs
> > add overhead.
> >
> > Let's pretend we have a vast volume of legacy code with a class that exposed
> > public data members, and yet we need to change the behaviour of the class
> > without either changing its size or shape. Let's say it's a struct with two
> > floats, and they can't be touched, but we need to intercept them being read
> > or written. We could make the floats private, and expose the two original
> > member variable names as proxy objects exposed as empty members within an
> > anonymous union also containing the floats. We can trap all operations on
> > those members EXCEPT assign-copy. We can trap assign from float (c.x=1.f),
> > and cast to float (f=c.x), and assign from different member (c.x=d.y), but
> > not assign member from same member (c.x=d.x).
>
> So the feature you actually need is zero-size reference members, and
> the only reason unions got into the mix was that you thought you could
> use them to emulate the desired feature.
C++ already supports zero-sized data members - they are called member
functions. A C++ member function is much more than a notational
convenience for programmers. Instead, member functions - along with
constructors, destructors and operator overloads - comprise C++'s type
system. And it is this type system that makes writing a program in C++
a fundamentally different experience than writing a program in C.
Whereas a C programmer may have little choice but to use a union as C
defines it in order to solve a particular problem - a C++ programmer in
the same situation has an additional option: that is, not to use the
built-in union type but to write a replacement. In other words, the
completeness of C++ type's type system does not at all handicap the C++
programmer - but in fact it has completely the opposite effect. The
relative completeness of C++'s user-defined, extensible type system is
empowering: it frees the programmer from reliance on built-in types and
their behaviors - or to put it another way: the C++ programmer can
effectively extend the language itself in order to solve problems.
And it is not too difficult to see how the C++'s type system could
implement a union-like replacement. Simply chnage the d0 and d1
instance variables in the example program into class methods; aside
from some minor adjustment to syntax, the client code remains
unchanged. The implementation of the union-like class itself though
improves significantly (by eliminating the union, the data member
const-ness, and the helper class template):
#include <cstdio>
using std::printf;
struct Data
{
int data[2];
Data(int a=0, int b=0)
{
data[0] = a;
data[1] = b;
}
};
class Twin
{
private:
Data twin;
public:
Twin() {}
Twin(int a, int b)
: twin( Data(a, b)) {}
Twin& operator=(const Twin& t)
{
twin = t.twin;
return *this;
}
int& d0() { return twin.data[0]; }
int d0() const { return twin.data[0]; }
int& d1() { return twin.data[1]; }
int d1() const { return twin.data[1]; }
};
int main()
{
Twin u(123,456), v(7,8);
printf("u.d0() = %d, u.d1() = %d\n", u.d0(), u.d1());
u.d0() = v.d0(); // u.d1() should be left unaffected
printf("u.d0() = %d, u.d1() = %d\n", u.d0(), u.d1());
printf("sizeof(Twin) = %d, ", sizeof(Twin));
printf("sizeof(Data) = %d\n", sizeof(Data));
}
Program Output:
u.d0() = 123, u.d1() = 456
u.d0() = 7, u.d1() = 456
sizeof(Twin) = 8, sizeof(Data) = 8
Greg
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: crosbie@digitalproductions.co.uk ("Crosbie Fitch")
Date: Tue, 13 Jun 2006 21:50:40 GMT Raw View
"Greg Herlihy" <greghe@pacbell.net> wrote in message
news:1150229948.275310.210340@f6g2000cwb.googlegroups.com...
> And it is not too difficult to see how the C++'s type system could
> implement a union-like replacement. Simply chnage the d0 and d1
> instance variables in the example program into class methods; aside
> from some minor adjustment to syntax, the client code remains
> unchanged.
It may be a 'minor adjustment to syntax' to you, but not to me.
You could of couse do this to remedy it:
#define d0 d0()
#define d1 d1()
But, that would be wicked.
Thanks for trying to persuade me of the merits of the traditional approach
anyway. :-)
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: johnchx2@yahoo.com
Date: Tue, 13 Jun 2006 18:06:23 CST Raw View
Greg Herlihy wrote:
> C++ already supports zero-sized data members - they are called member
> functions.
[snip]
> And it is not too difficult to see how the C++'s type system could
> implement a union-like replacement. Simply chnage the d0 and d1
> instance variables in the example program into class methods; aside
> from some minor adjustment to syntax, the client code remains
> unchanged.
I mostly agree with this, except...there's an argument to be made that
there's a big difference between changing client code (however small
the change) and not changing client code. A small change that has to
be made in a couple of hundred different places could be a big burden.
(Especially if the change isn't the sort of thing that can be safely
done with text-based search-and-replace.)
Personally, I think that being able to assign a (non-function) name to
an arbitrary sub-object of a class opens up some interesting design
possibilities. I'm not sure it buys you anything *fundamentally*
different from what you could get with a member function, but I think
that at least some people would find the non-function syntax easier to
work with.
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: "Greg Herlihy" <greghe@pacbell.net>
Date: Tue, 13 Jun 2006 20:38:25 CST Raw View
johnchx2@yahoo.com wrote:
> Greg Herlihy wrote:
>
> > C++ already supports zero-sized data members - they are called member
> > functions.
>
> [snip]
>
> > And it is not too difficult to see how the C++'s type system could
> > implement a union-like replacement. Simply chnage the d0 and d1
> > instance variables in the example program into class methods; aside
> > from some minor adjustment to syntax, the client code remains
> > unchanged.
>
> I mostly agree with this, except...there's an argument to be made that
> there's a big difference between changing client code (however small
> the change) and not changing client code. A small change that has to
> be made in a couple of hundred different places could be a big burden.
> (Especially if the change isn't the sort of thing that can be safely
> done with text-based search-and-replace.)
Given that the class is apparently (very much) a work-in-progress, it
remains unclear how much existing client code there is - or whether
that code would have to be changed anyway to bring it over to C++.
Moreover, this particular change is not prone to error. Because any
code not updated will fail to compile. So while the change itself would
no doubt be tedious to apply to a large amount of code, the risk of
error is reasonably low.
I do agree that the parenthesized syntax is not ideal. It is more
common in fact for an interface to separate the read and write property
methods into separate functions - instead of overloading them.
And other solutions are no doubt possible. For example, implementing
other types of member functions may also be worth considering.
Overloading the [] operator for an enumerated value could fit well with
certain data models:
Twin u;
u[d1] = 5;
> Personally, I think that being able to assign a (non-function) name to
> an arbitrary sub-object of a class opens up some interesting design
> possibilities. I'm not sure it buys you anything *fundamentally*
> different from what you could get with a member function, but I think
> that at least some people would find the non-function syntax easier to
> work with.
Well, it also possible to "invert" the function call syntax by using
conversion operators:
Twin u;
int i = d1(u);
Here d1 is now "outside" instead of "within" the value object, u. I am
not sure that this approach has much to recommend it though.
But however the solution is implemented, I am sure that we can both
agree that the approach should be one that works within C++'s type
system and not one that tries to work around it.
Greg
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: johnchx2@yahoo.com
Date: Wed, 7 Jun 2006 13:44:07 CST Raw View
"Crosbie Fitch" wrote:
> <johnchx2@yahoo.com> wrote in message
>
> > Actually, unions *are* class types.
>
> Ok, let's be more precise: unions may not have user defined methods.
Huh?!
Quoting 9.5/1: "A union can have member functions (including
constructors and destructors), but not virtual (10.3) functions."
> Such prohibitions had never been made to prevent out of order access to
> union members,
Mainly because such a prohibition is unenforceable without
whole-program analysis, which the C++ standard is careful to avoid
requiring.
> Incidentally, given union { int a,b; } I'd suggest that it's not so much the
> order in which members are read or initialised that's important, it's that a
> read member is congruent in memory with an initialised representation of the
> same type (subject to whether such congruence can be known by the
> programmer).
I gather that the intent at the time was to permit implementations
which would keep track of the "active" member at runtime and raise an
error if the "wrong" member were read.
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: johnchx2@yahoo.com
Date: Wed, 7 Jun 2006 15:11:43 CST Raw View
"Crosbie Fitch" wrote:
> So, now I have to go through the hoops:
>
> class A
> {
> A& CustomAssignCopy(const A& a) { /*copy*/ return *this; }
> public:
> A& operator=(const A& a) const { return
> const_cast<A*>(this)->CustomAssignCopy(a); }
> };
>
> union U
> {
> A a;
> };
>
>
> THAT IS PERMITTED and compilers quite happily compile it,
I can't see why that would be legal, nor does either g++ (3.4.2) or
comeau online. What compiler accepts this?
> In my case I'm not even wanting to exploit undefined behaviour. I need a
> custom assign-copy operator precisely in order to ensure the correct
> representation within the union is being assigned to.
I'm curious to know more about the specific problem at hand. I gather
that you want to include in a union a type which is otherwise POD but
which requires a custom copy assignment operator. I'm having trouble
envisioning what that would look like. Can you elaborate?
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: kuyper@wizard.net
Date: Wed, 7 Jun 2006 15:11:27 CST Raw View
"Crosbie Fitch" wrote:
> <johnchx2@yahoo.com> wrote in message
> news:1149633364.561734.56320@i39g2000cwa.googlegroups.com...
> > memcpy doesn't work on non-PODs.
> > This is because they aren't guaranteed to be laid out anything like you
> > think they will be.
>
> 'memcpy doesn't work on non-PODs' !!!
More accurately, the standard guarantees the success of memcpy() only
for POD types. It may happen to work for particular non-POD types on a
particular implementation, but since success isn't guaranteed,
competent programmers avoid relying upon it.
> If memcpy doesn't work on non-POD objects then something truly fundamental
> has changed, e.g. C++ has become C#.
No, the change that occurred was that C became C++, and apparantly you
missed that change. However, the problem exists (in a somewhat
different form) even in C. The simplest example I can think of is an
object which contains a pointer into itself. When such an object is
copied using memcpy(), the copied object's pointer now points into the
original object, instead of into itself. member functions that rely on
where that pointer points will malfunction, possibly in quite horrible
ways. Fixing the pointer to refer to the appropriate location in the
copy is one example of why you would want to define a non-trivial
copy-constructor or copy-assignment operator.
Another example of this is any class type that contains a pointer to
dynamically allocated memory, which it is the destructor's
responsibility to deallocate(). If you use the copy constructor or copy
assignment operator for that class type, it will generally allocate new
memory for the copy, and fill in that memory with a copy of what it the
original pointer pointed at (though other approaches are possible,
including COW). If you use memcpy(), you'll have two copies of the
pointer, when the member function code for that class will almost
certainly be assuming it owns the only copy of the pointer. In
particular, you'll have undefined behavior if you execute the
destructor for both copies, and a memory leak if you don't invoke the
destructor on either copy. One solution would be to execute the
destructor on only the original. I'll leave it as an excercise for the
student to devise a class where that "solution" doesn't work. If it
takes you more than 15 minutes to solve that exercise, I'd recommend
chosing a different profession.
Of course, you can do exactly the same thing in a POD, using regular
functions instead of member functions to perform construction, copying,
and destruction. The difference is mainly a matter of philosophy. For
class types, the member functions are considered to be a part of the
object; the fact making a copy with memcpy() makes an object for which
those functions can't operate correctly is considered to be damage to
the object, even though the bit pattern of the object is unchanged. For
a POD type, routines that you could write for construction, copying, or
destroying the object are considered to be independent from the object,
so the fact that they would malfunction can be considered an error due
to the fact that you called those routines, rather than damage that was
caused by memcpy().
There are other more complicated ways in which a non-POD object can
fail to work properly after being copied with memcpy(), but those are
the two simplest ones I could think of.
> I don't care how non-POD objects are laid out, because I don't intend to
> exploit their layout. I only require that I have the option that I may
> overlay their storage with other data, AND that I be given credit for
> knowing that the non-POD objects may require construction with placement
> new, may require custom non-default assignment, and may require destruction.
And also, you'd need to know when you can and cannot copy a non-POD
type with memcpy().
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: "Crosbie Fitch" <crosbie@digitalproductions.co.uk>
Date: Wed, 7 Jun 2006 16:48:02 CST Raw View
"James Dennett" <jdennett@acm.org> wrote in message
news:4BChg.102685$iU2.73289@fed1read01...
> It is not valid to use memcpy on non-PODs. If you
> choose to do it, you cannot portably cast the resulting
> memory to the non-POD type and expect to use it.
Unless, you wrote that non-POD class, and know that memcpy is safe for it.
> This is nothing new, and C++ still isn't C#. It's not
> C either; C++'s object model is layered on top of C's,
> but non-PODs are a whole world of their own.
I appreciate the complications of C++ in terms of the unknowability of
underlying representations, however, I don't appreciate brick walls being
erected to prevent programmers venturing into risky areas.
template <class NONPOD>
class Risky
{
union
{ NONPOD a; // Currently disallowed
};
Risky() { new(&a) NONPOD(); }
~Risky() { a.NONPOD::~NONPOD(); }
Risky& operator=(const Risky& r) { a=r.a; }
};
foo()
{ Risky<NP> a,b;
a=b;
}
Sods law says I've made a boo-boo, but you get the idea. The class can have
a selector added and additional union members.
There are non-POD classes NP for which the above is safe.
Out of interest, are there any non-POD classes NP (nevertheless compliant
with the template) for which the above code is unsafe?
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: johnchx2@yahoo.com
Date: Wed, 7 Jun 2006 16:49:53 CST Raw View
"Crosbie Fitch" wrote:
> There are obviously plenty of objects for which memcpy results in an invalid
> object, e.g. the object may contain a pointer to itself (a member of a
> linked list, say), however, it still copies the storage representing that
> object - and if put back at the original address should recreate it ok
> (unless it had volatile members, etc.).
It's not clear to me whether you think this guarantee already exists in
the C++ standard or you are asking that it be added to the standard.
As it is currently written, the guarantee you're talking about
(round-trip memcpy) is only available for PODs (3.9/2). There is no
such guarantee for non-PODs in C++.
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: kuyper@wizard.net
Date: Wed, 7 Jun 2006 16:49:34 CST Raw View
"Crosbie Fitch" wrote:
> <kuyper@wizard.net> wrote in message
> news:1149623054.476991.128910@h76g2000cwa.googlegroups.com...
> > "Crosbie Fitch" wrote:
> >> Apart from overlaid storage there should be no difference between:
> >> struct S { A a; B b; } s; s.b=B(123);
> >> and
> >> union U { A a; B b; } u; new(&u.b) B(); u.b=B(123);
> >
> > So a default constructor would be mandatory?
>
> No. There would be no restrictions on what methods a member had or did not
> have.
What I really meant to say is that I'm surprised you didn't use
new(&u.b) B(123);
However, the struct case you gave also requires that B have an
accessible default constructor, so that's not really a difference
between the two cases.
However, there is an issue with default constructors, I just didn't
raise the issue correctly. The struct case you gave calls the default
constructors for both A and B (if any). I'm curious what rules you're
proposing for the handling of unions of non-POD types, that would
produce that same sequence of constructor calls. I can imagine rules
that involve one call to A(), two calls to B(), and one call to B(123).
I can imagine another set of rules that produces just one call B().
However, I can't come up with reasonable rules that produce one call to
A() and one call to B(), which is what would happen in the struct case
you game. You said those two cases should have the same behavior.
Except when the default constructors for both A and B are no-ops, that
doesn't seem possible. And, when they are non-trivial, it's quite
dangerous, since it would result in two different objects being
constructed in the same memory without an intervening destructor call.
.
> Until unions are permitted to have user defined methods like classes, the
> defaults have to be the C defaults, i.e. no construction/destruction and
> bitwise assign-copy (of the memory - not the members).
That's what the POD rules are for. I thought that you wanted that
restriction removed?
> Has no-one considered that a class may be designed precisely with a view to
> its appearance within a union?
For non-POD types, C++ has abolished every guarantee that makes it
useful to view it through a union. In particular, the fact that
memcpy() is only required to work on POD types, and that sizeof() is
only required to produce meaningful results on POD types, allows an
implementation where the memory in which a non-POD object is stored is
non-contiguous.
> > Despite this fact, the union itself is to be copied bitwise.
> > Could you explain in a little more detail precisely how that's supposed
> > to work?
>
> The union is large enough to contain the representation of its largest
> member.
> Copying a union bitwise by default is just as dangerous as a default
> assign-copy operator for a class (with member pointers).
I'm talking about classes with non-trivial assignment copy operators,
where copying them bit-wise could be a whole lot more dangerous than
that. It's precisely because memcpy() won't do the right thing that the
class has a non-trivial assignment copy operator.
> > I'm curious - who expressed that delusion? That sounds like a
> > description of the implicitly-defined copy assignment operator for
> > class types, but it doesn't describe unions.
>
> Are you really telling me that you believe that when copying a union each
> member's default assign-copy operator is called?
No, you described that as a delusion, and I was agreeing with you.
However, your wording implied that someone has actually promoted that
delusion. I was just asking you who that was.
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: crosbie@digitalproductions.co.uk ("Crosbie Fitch")
Date: Wed, 7 Jun 2006 22:13:53 GMT Raw View
<johnchx2@yahoo.com> wrote in message
news:1149697711.424919.193820@h76g2000cwa.googlegroups.com...
> Quoting 9.5/1: "A union can have member functions (including
> constructors and destructors), but not virtual (10.3) functions."
Given I've been dealing primarily with anonymous unions (which can't have
member functions) I assumed that named unions couldn't have them either.
Mea culpa.
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: "Bo Persson" <bop@gmb.dk>
Date: Wed, 7 Jun 2006 17:49:52 CST Raw View
""Crosbie Fitch"" <crosbie@digitalproductions.co.uk> skrev i
meddelandet news:WrCdnVitasvDYhvZRVnysA@bt.com...
> "Bo Persson" <bop@gmb.dk> wrote in message
> news:4eo9kjF1f86otU1@individual.net...
>> How would it work?!
>> class nonPOD
> ..
>> This class can be constructed, but it cannot be copied or assigned
>> (because the copy constructor and assignment operator are private).
>
> Let's not get confused by copying an object and copying the storage
> representing the object.
Ok, you can copy the bytes of an object to a char array, say (using
memcpy). That's fine if you want to inspect the bytes making up an
object. It doesn't give you another object though.
>
> I'm not saying that memcpy can safely copy any object, I'm saying it
> can copy the memory representing a non-POD object, i.e. one with a
> non-default assign-copy operator. Just because it has a user defined
> assign-copy operator doesn't suddenly make it unsafe to memcpy.
Sure, some non-PODs could theoretically be copied bit-wise, but the
language standard doesn't say exactly which ones would be ok. I
believe that the committee didn't find it important enough to spend
time on sorting it out. Therefore the rules are that PODs work
(because they do in C), and non-PODs doesn't work because we don't
know what will happen.
That is exactly what "undefined behaviour" means - it is not defined.
:-)
>
> It can only safely copy an object if the person using memcpy knows
> that a bitwise copy is a safe operation despite the object having a
> non-default assign-copy operator.
Unless he knows the compiler writer on a particular system, how can he
be sure that it works?
Also, if a struct is simple enogh to be memcpy'd, it is not
unreasonable that the compiler knows that too, and optimizes a
memberwise copy into a memcpy. Happens all the time!
>
> There are obviously plenty of objects for which memcpy results in an
> invalid object, e.g. the object may contain a pointer to itself (a
> member of a linked list, say), however, it still copies the storage
> representing that object - and if put back at the original address
> should recreate it ok (unless it had volatile members, etc.).
>
> Unfortunately, the definition of POD objects, whilst always safe to
> memcpy, exclude many objects with non-default assign-copy operators
> that are also safe to memcpy and/or safe to exist as members of a
> union.
And as we don't have a standard list of exactly when it must work, we
just don't know.
Bo Persson
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: crosbie@digitalproductions.co.uk ("Crosbie Fitch")
Date: Thu, 8 Jun 2006 03:15:17 GMT Raw View
<johnchx2@yahoo.com> wrote in message
news:1149697711.424919.193820@h76g2000cwa.googlegroups.com...
> Quoting 9.5/1: "A union can have member functions (including
> constructors and destructors), but not virtual (10.3) functions."
Sheesh. You learn something every day.
Thanks for enlightening me.
Now there's even less of a case to prohibit non-POD members.
> I gather that the intent at the time was to permit implementations
> which would keep track of the "active" member at runtime and raise an
> error if the "wrong" member were read.
I think that's going beyond C/C++'s remit. Such logic and the memory
necessary to support it really belongs in the programmer's domain. The
programmer simply needs the components with which to construct such a beast.
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: johnchx2@yahoo.com
Date: Wed, 7 Jun 2006 22:15:51 CST Raw View
"Crosbie Fitch" wrote:
> However, if I know that class A is POD, and only becomes non-POD due to the
> presence of a non-default assign-copy operator, then memcpy can copy that
> object and I can invoke methods on the copy.
You don't seem to understand: once a type is non-POD, the compiler is
allowed to do things to it that mean that memcpy won't produce the
results you expect, without telling you. You *can't* know that it's
safe to memcpy just by inspecting it. The compiler has a lot of leeway
to do things under-the-hood. This doesn't depend upon anything that
you, the programmer, can control.
> Anyway, this is a tangent. I just wanted to point out that simply being
> non-POD does not prevent memcpy creating a valid copy. It also has to be an
> object for which memcpy is unsafe (or inherently invalid).
You are misinformed. Once again, the compiler is free to introduce
changes into the non-POD that make it unsafe to memcpy, without
informing you.
> The set of objects for which memcpy always results in a valid object is
> larger than the set of POD objects.
If by "always" you mean "guaranteed by the C++ standard," then you are
simply incorrect. Of course, if you can find the guarantee somewhere
in the standard, then I'll happily eat my words. But I'll need a
precise citation.
> > I suppose it depends on your definintion of "work" of course. memcpy
> > will indeed copy some bytes. But there's no guarantee that the result
> > is useable.
>
> Indeed. It requires knowledge of the class being copied.
>
It requires more than that -- it requires you to assume things about
the object model that the C++ standard goes out of its way NOT to
guarantee.
> 1a) Until the union assign-copy operator can be overloaded, it is best
> thought of as a memcpy rather than a simultaneous call to all members'
> assign-copy operators.
Well, a union copy assignment operator *can* be overloaded.
> 1b) That unions with non-POD members may often require explicit
> construction, copying, and destruction should not mean that they are simply
> prohibited from being union members. Rather, this typical requirement should
> be brought to the attention of programmers who make them members.
>
> Any non-POD object can be a member of a union PROVIDED it is correctly
> constructed, copied and destroyed (rather than use the default memcpy, and
> no default construction/destruction).
>
> So, please permit anonymous unions to contain non-POD members - given that
> the user of an anonymous union will be clearly obliged to handle its
> members' construction, assignment-copying, and destruction.
It sounds like your practical requirements are really for a
discriminant union that allows non-POD types, which you really can
have...it just doesn't happen to be the built-in union. boost::variant
is an example, if you aren't already familiar with it:
http://www.boost.org/doc/html/variant.html
Implementing such a thing isn't a trivial task (you do have to jump
through some hoops to avoid undefined behavior), but the result is both
safe and fairly easy to use. It's also arguably optimally
efficient...I don't think you'd get any performance improvement rolling
your own with built-in unions.
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: "Crosbie Fitch" <crosbie@digitalproductions.co.uk>
Date: Thu, 8 Jun 2006 08:27:30 CST Raw View
<kuyper@wizard.net> wrote in message
news:1149702049.000782.69720@c74g2000cwc.googlegroups.com...
> What I really meant to say is that I'm surprised you didn't use
> new(&u.b) B(123);
For clarity. I wanted to demonstrate where the difference occurred, i.e.
that a union did not call the default constructor - that it must be called
explicitly - rather than simply prohibit a non-default.
> I'm curious what rules you're
> proposing for the handling of unions of non-POD types, that would
> produce that same sequence of constructor calls.
I'm saying that a union should not attempt to construct its members.
If I have 10 members of a union, are you really suggesting to me that each
default constructor is called that coincidentally does nothing?
>> Until unions are permitted to have user defined methods like classes, the
>> defaults have to be the C defaults, i.e. no construction/destruction and
>> bitwise assign-copy (of the memory - not the members).
>
> That's what the POD rules are for. I thought that you wanted that
> restriction removed?
I'm distinguishing between the union's constructor and the constructors of
the union's members.
The union's constructor should by default do nothing - which is currently
misportrayed as actually calling each of its members default constructors
which coincidentally do nothing.
> For non-POD types, C++ has abolished every guarantee that makes it
> useful to view it through a union. In particular, the fact that
> memcpy() is only required to work on POD types, and that sizeof() is
> only required to produce meaningful results on POD types, allows an
> implementation where the memory in which a non-POD object is stored is
> non-contiguous.
So, you're saying that if I have
struct S
{
int i;
S& operator=(const S& s) { i=s.i; }
};
that even if I was allowed to put it in a union, there's no way the compiler
can compute the size of S or allocate a large enough union to contain S?
That is one lousy compiler.
How about this one:
template <class POD>
struct Empty: POD
{
Empty& operator=(const Empty& e) { PODMethod(e); }
};
If the other member of the union is an instance of POD, why can't Empty
(which introduces the dreaded non-pod assignment and no additional members)
call a POD safe method on its POD base?
> I'm talking about classes with non-trivial assignment copy operators,
> where copying them bit-wise could be a whole lot more dangerous than
> that. It's precisely because memcpy() won't do the right thing that the
> class has a non-trivial assignment copy operator.
Let's get this straight: I agree that memcpy() is not guaranteed to copy any
non-POD object.
I'm saying that irrespective of that, that's no reason to prohibit a non-POD
member within an anonymous union. Rather than prohibit it, simply say that
memcpying an anonymous union containing a non-POD member is undefined.
Moreover, you can say that a class that contains an anonymous union with a
non-POD member will not have a default assign-copy operator or copy
constructor (or default constructor or destructor).
> However, your wording implied that someone has actually promoted that
> delusion. I was just asking you who that was.
I inferred it from the restrictions governing unions and their members.
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: francis@robinton.demon.co.uk (Francis Glassborow)
Date: Thu, 8 Jun 2006 13:26:17 GMT Raw View
In article <yZydna6_udi7mhrZRVnytw@bt.com>, Crosbie Fitch
<crosbie@digitalproductions.co.uk> writes
><johnchx2@yahoo.com> wrote in message
>news:1149695391.355185.314920@j55g2000cwa.googlegroups.com...
>> The line "a->method();" has undefined behavior. There is no guarantee
>> that a points to a valid object of class A. (Assuming class A is
>> non-POD.)
>
>However, if I know that class A is POD, and only becomes non-POD due to the
>presence of a non-default assign-copy operator, then memcpy can copy that
>object and I can invoke methods on the copy.
>
>Anyway, this is a tangent. I just wanted to point out that simply being
>non-POD does not prevent memcpy creating a valid copy. It also has to be an
>object for which memcpy is unsafe (or inherently invalid).
>
>The set of objects for which memcpy always results in a valid object is
>larger than the set of POD objects.
That is an assertion that is not, AFAICS, supported by the C++ Standard.
An implementation is entitled to do many things with a non-POD that it
is not allowed to do to a POD. Not least it is entitled to include
unique object identifiers which could possible be useful for debugging.
For example, such unique identifiers could be used to trap aliasing
through pointers or references.
--
Francis Glassborow ACCU
Author of 'You Can Do It!' and "You Can Program in C++"
see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: crosbie@digitalproductions.co.uk ("Crosbie Fitch")
Date: Thu, 8 Jun 2006 13:41:42 GMT Raw View
<johnchx2@yahoo.com> wrote in message
news:1149706018.117223.42630@u72g2000cwu.googlegroups.com...
> "Crosbie Fitch" wrote:
>> The set of objects for which memcpy always results in a valid object is
>> larger than the set of POD objects.
>
> If by "always" you mean "guaranteed by the C++ standard," then you are
> simply incorrect. Of course, if you can find the guarantee somewhere
> in the standard, then I'll happily eat my words. But I'll need a
> precise citation.
I'm quite comfortable with the standard not guaranteeing memcpy being able
to copy a non-POD object. That doesn't mean that there are no non-POD
objects that survive a memcpy on most compilers. I revise this to 'most',
because I can imagine that some compilers may well now take advantage of the
standard's POD definition and the moment a non-default assign-copy operator
is added bung a wodge of location sensitve data into the resulting object
(or create some separate garbage collection/diagnostic table somewhere).
<gasp>
I'm not asking for any guarantees. Or trying to create guarantees that the
standard doesn't provide.
I'm simply challenging its prohibition against non-POD members appearing
within an anonymous union - given the programmer can ensure they don't use
memcpy to copy those non-POD members.
> It requires more than that -- it requires you to assume things about
> the object model that the C++ standard goes out of its way NOT to
> guarantee.
A standard should say what is and is not defined or guaranteed. It should
not attempt to prohibit certain things because programmers may not realise
that non-POD objects must be correctly initialised, copied and destroyed.
Let the compiler do the warning.
Standards are frustrating when they butt in and say "Thou shalt not be
permitted to have this particular method in this particular circumstance"
instead of saying "In this particular circumstance, having this particular
method, makes memcpy invalid - ensure you use the member's assign-copy
operator". The standard should not prevent legitimate code simply to
increase code safety. C++ is not an educational language.
> It sounds like your practical requirements are really for a
> discriminant union that allows non-POD types, which you really can
> have...it just doesn't happen to be the built-in union. boost::variant
> is an example, if you aren't already familiar with it:
I think what I want is 'overlay', which co-incidentally happens to be
identical to an anonymous union that permits non-pod members.
overlay
{
type1 name1;
..
typen namen;
};
An overlay occupies sufficient memory to contain its largest member. No
initialisation, copying, or destruction of members is performed by default.
An overlay is not a type, but a storage/alignment instruction. It may only
appear within a class.
Use of any overlaid name-x is equivalent to
reinterpret_cast<type-x&>(overlay). The normal rules of reinterpret_cast
apply.
There is no restriction on the members or types within an overlay. If any
member is volatile in whole or part, the entire overlay is considered
volatile.
Appearance of overlay within a class causes that class to be non-POD.
Use of overlay with a single member is a means of disabling default
construction/copying/destruction for that member.
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: "Crosbie Fitch" <crosbie@digitalproductions.co.uk>
Date: Thu, 8 Jun 2006 09:58:18 CST Raw View
"Francis Glassborow" <francis@robinton.demon.co.uk> wrote in message
news:jqf3kwzaTAiEFwcB@robinton.demon.co.uk...
> In article <yZydna6_udi7mhrZRVnytw@bt.com>, Crosbie Fitch
>>The set of objects for which memcpy always results in a valid object is
>>larger than the set of POD objects.
>
> That is an assertion that is not, AFAICS, supported by the C++ Standard.
> An implementation is entitled to do many things with a non-POD that it is
> not allowed to do to a POD. Not least it is entitled to include unique
> object identifiers which could possible be useful for debugging. For
> example, such unique identifiers could be used to trap aliasing through
> pointers or references.
You're absolutely right.
However, I am not asserting the C++ standard. Nor am I asking the C++
standard to support my assertion.
If you write a program (theoretically) that contains the set of all possible
objects, memcpys each one, and run it on all compilers, then there will be
far more runtime-valid objects that result than the standard guarantees
(more than just the POD ones).
All I was trying to point out was that just as reading an uninitialised
union member was not defined, so too memcpying a nonPOD object may not be
defined, and yet with prudence and pragmatic consideration for the target
compilers/platforms, such undefined things can be done. memcpying an object
that is POD but for the presence of a non-default assign-copy operator can
often be relied upon to work. If it doesn't work then you're probably using
one of those new-fangled debugging/conformance validating compilers, i.e.
you'll know that for compiler X/diagnostics-mode[n] memcpy results in
failure.
ANYWAY
I don't want to memcpy nonPOD objects! Never have done, never want to.
I was obviously being very reckless in daring to suggest that memcpy could
indeed copy certain nonPOD objects. Obviously, this is contrary to the
standard - never disputed that. However, if every compiler, at compile time
or runtime, refused to permit the use of memcpy on POD-like non-POD
objects - despite it actually resulting in valid objects sometimes - then a
lot of code would break, and there'd be a lot of unhappy bunnies. Not me,
because I'd never do such a thing - of course.
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: Francis Glassborow <francis@robinton.demon.co.uk>
Date: Thu, 8 Jun 2006 09:57:32 CST Raw View
In article <4eoj5gF1g0nusU1@individual.net>, Bo Persson <bop@gmb.dk>
writes
>Sure, some non-PODs could theoretically be copied bit-wise, but the
>language standard doesn't say exactly which ones would be ok. I
>believe that the committee didn't find it important enough to spend
>time on sorting it out. Therefore the rules are that PODs work
>(because they do in C), and non-PODs doesn't work because we don't
>know what will happen.
Let me refine that statement to ' The C++ Standard places no
requirements on code that includes a union with one or more members of a
non-POD type.' The time and verbiage that would be required to specify
the safe non-POD cases could not be justified by a cost-benefit
analysis. Even an hour of WG21 time is scarily expensive and it would
take much more than an hour to tie down this problem to the benefit of
very few.
I could make a good case for introducing discriminated unions into C++
but have enough other projects to keep me fully occupied.
--
Francis Glassborow ACCU
Author of 'You Can Do It!' and "You Can Program in C++"
see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: francis@robinton.demon.co.uk (Francis Glassborow)
Date: Thu, 8 Jun 2006 14:57:55 GMT Raw View
In article <KaWdnVJeYIVWlhrZnZ2dnUVZ8s6dnZ2d@bt.com>, Crosbie Fitch
<crosbie@digitalproductions.co.uk> writes
>I appreciate the complications of C++ in terms of the unknowability of
>underlying representations, however, I don't appreciate brick walls being
>erected to prevent programmers venturing into risky areas.
There is nothing to stop you venturing into risky areas, it is just that
the C++ Standard is not going to hold your hand while you do so.
--
Francis Glassborow ACCU
Author of 'You Can Do It!' and "You Can Program in C++"
see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: Francis Glassborow <francis@robinton.demon.co.uk>
Date: Thu, 8 Jun 2006 10:15:05 CST Raw View
In article <pLidnYIYl-d8hRXZnZ2dnUVZ8qidnZ2d@bt.com>, Crosbie Fitch
<crosbie@digitalproductions.co.uk> writes
>> It requires more than that -- it requires you to assume things about
>> the object model that the C++ standard goes out of its way NOT to
>> guarantee.
>
>A standard should say what is and is not defined or guaranteed. It should
>not attempt to prohibit certain things because programmers may not realise
>that non-POD objects must be correctly initialised, copied and destroyed.
>Let the compiler do the warning.
There is no such thing as a mandated warning. All the C++ Standard
requires is diagnostics, which can simply be:
'Non-POD member of union -- you may get some surprising behaviour.'
However if you read the relevant sub-clauses carefully you may realise
that all C++ is doing is to provide minimal support for unions in C. I
think it is possible to design a class that does more (the class default
constructor would obtain a block of raw storage and a set of overloaded
assignments would handle the overlaying of members on that memory)
--
Francis Glassborow ACCU
Author of 'You Can Do It!' and "You Can Program in C++"
see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: crosbie@digitalproductions.co.uk ("Crosbie Fitch")
Date: Thu, 8 Jun 2006 16:42:49 GMT Raw View
"Francis Glassborow" <francis@robinton.demon.co.uk> wrote in message
news:pK71styIOAiEFw+L@robinton.demon.co.uk...
> There is nothing to stop you venturing into risky areas, it is just that
> the C++ Standard is not going to hold your hand while you do so.
Yes, well, I'd appreciate it if it didn't hold my hand when I'm using the
anonymous union and say "Oooh no. You can't put a non-POD member here. That
would require that you constructed, copied, and destroyed it all by
yourself."
It's precisely this hand-holding that has held me back.
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: kuyper@wizard.net
Date: Thu, 8 Jun 2006 11:44:37 CST Raw View
Crosbie Fitch wrote:
> <kuyper@wizard.net> wrote in message
> news:1149702049.000782.69720@c74g2000cwc.googlegroups.com...
> > What I really meant to say is that I'm surprised you didn't use
> > new(&u.b) B(123);
>
> For clarity. I wanted to demonstrate where the difference occurred, i.e.
> that a union did not call the default constructor - that it must be called
> explicitly - rather than simply prohibit a non-default.
But you said it would have the same effects as the corresponding struct
you showed, which does involve default constructor calls. I think you
might want to come up with some way of describing what you want to have
happen, that doesn't involve that comparison with a struct type.
> > I'm curious what rules you're
> > proposing for the handling of unions of non-POD types, that would
> > produce that same sequence of constructor calls.
>
> I'm saying that a union should not attempt to construct its members.
> If I have 10 members of a union, are you really suggesting to me that each
> default constructor is called that coincidentally does nothing?
I'm saying that if you allow non-POD types in your union, they either
run the risk of not having their constructors called, or of having
constructors called for overlapping objects without an intervening
destructor call. One of the key advantages of C++ over C is the
guarantees it provides about automatic construction and destruction of
objects, and I don't think it's a good idea to extend C unions to allow
members to have types for which those guarantees are both desireable
and impossible to provide.
.
> The union's constructor should by default do nothing - which is currently
> misportrayed as actually calling each of its members default constructors
> which coincidentally do nothing.
And for non-POD types, that violates the design of C++. Any object with
a non-trivial constructor should have that constructor called. Using
placement new to invoke the constructor is a hack. It's a necessary
hack, and I wouldn't recommend that it be dropped from the language,
but every time you use it you should be asking yourself whether there's
any other way to do what you want; if the answer is yes, it's probably
a better way of doing that task. Allowing non-POD types in unions only
becomes workable by use of placement new, or equivalent mechanisms.
> > For non-POD types, C++ has abolished every guarantee that makes it
> > useful to view it through a union. In particular, the fact that
> > memcpy() is only required to work on POD types, and that sizeof() is
> > only required to produce meaningful results on POD types, allows an
> > implementation where the memory in which a non-POD object is stored is
> > non-contiguous.
>
> So, you're saying that if I have
> struct S
> {
> int i;
> S& operator=(const S& s) { i=s.i; }
> };
>
> that even if I was allowed to put it in a union, there's no way the compiler
> can compute the size of S or allocate a large enough union to contain S?
> That is one lousy compiler.
No. I'm saying that the implementation is ALLOWED to implement non-PODs
in such a way that, for some reason or another, there's no number that
sizeof could return for which memcpy(&target, &original, sizeof
original) will result in a correctly working copy of 'original' being
stored in 'target'. Looking at your definition of 'S', I can't imagine
any reason why they would want to do so in this case, but the standard
says that if an implementation did decide to do so, it would be legal.
The key point is that the standard provides no basis for deciding which
non-POD types have meaningful sizeof values, and which ones can be
safely memcpy()d. A prudent programmer therefore doesn't even try to
guess; when working with types that are, or at least might be, non-POD
types, the prudent programmer uses copy assignment or copy construction
to copy objects of those types.
> How about this one:
>
> template <class POD>
> struct Empty: POD
> {
> Empty& operator=(const Empty& e) { PODMethod(e); }
> };
Anything which renders a class a non-POD type gives an implementation
license to implement it in ways that render sizeof meaningless and
memcpy dangerous. An implementation isn't required to do so, but the
fact that it's permitted should be enough justification to avoid
relying on sizeof and memcpy for non-POD types.
> > However, your wording implied that someone has actually promoted that
> > delusion. I was just asking you who that was.
>
> I inferred it from the restrictions governing unions and their members.
Could you explain that chain of inference?
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: "Crosbie Fitch" <crosbie@digitalproductions.co.uk>
Date: Thu, 8 Jun 2006 13:25:53 CST Raw View
"Francis Glassborow" <francis@robinton.demon.co.uk> wrote in message
news:h663wFyPMAiEFw8N@robinton.demon.co.uk...
> Let me refine that statement to ' The C++ Standard places no requirements
> on code that includes a union with one or more members of a non-POD type.'
Well, saying "disallows non-POD members" is a tad easier than citing this
lot each time:
"An object of a class with a non-trivial constructor (class.ctor), a
non-trivial copy constructor (class.copy), a non-trivial destructor
(class.dtor), or a non-trivial copy assignment operator (over.ass,
class.copy) cannot be a member of a union, nor can an array of such
objects."
From: http://www.kuzbass.ru:8086/docs/isocpp/class.html
> I could make a good case for introducing discriminated unions into C++ but
> have enough other projects to keep me fully occupied.
You don't have to go the whole hog. I'm sure plenty of folk would be happy
just to have non-POD anonymous unions. You can easily create a discriminated
union from there.
I am not alone:
http://lists.boost.org/Archives/boost/2002/02/25409.php
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: "Crosbie Fitch" <crosbie@digitalproductions.co.uk>
Date: Thu, 8 Jun 2006 14:26:46 CST Raw View
"Francis Glassborow" <francis@robinton.demon.co.uk> wrote in message
news:baf5os$ZsDiEFwI1@robinton.demon.co.uk...
> There is no such thing as a mandated warning. All the C++ Standard
> requires is diagnostics, which can simply be:
>
> 'Non-POD member of union -- you may get some surprising behaviour.'
That's fine. Unfortunately it doesn't do this, it says "Anonymous unions are
not permitted to have non-POD members".
:-(
> However if you read the relevant sub-clauses carefully you may realise
> that all C++ is doing is to provide minimal support for unions in C. I
> think it is possible to design a class that does more (the class default
> constructor would obtain a block of raw storage and a set of overloaded
> assignments would handle the overlaying of members on that memory)
class Disc
{
union
{ opaqe data;
data_method<opaque> foo; // foo is empty or derived from opaque
};
} c,d;
I want c.foo=123 to call data.assign(123);
I can do that - no problem.
I want c.foo=d.foo to call data.assign(d.foo);
Unfortunately it calls data_method<opaque>::operator=(const
data_method<opaque>&) - and this is the one key method of foo that I have
been prohibited from overriding.
If you can give me a class Disc' that has the same member foo with any set
of methods/operators and where the class has sizeof(opaque) then I'll be
very grateful.
My planned work-around is this:
class Disc
{
union
{ opaqe data;
const data_method<opaque> foo; // const!!
};
} c,d;
c.foo=d.foo will not call data_method<opaque>::operator=(const
data_method<opaque>&) if it can instead convert RHS into a type accepted by
a non-default const assignment operator.
All I need to figure out next is to partially specialise this class such
that constant instances don't have assignment methods.
It's a nasty work-around, but someone's got to do it.
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: johnchx2@yahoo.com
Date: Thu, 8 Jun 2006 14:29:59 CST Raw View
"Crosbie Fitch" wrote:
> Standards are frustrating when they butt in and say "Thou shalt not be
> permitted to have this particular method in this particular circumstance"
> instead of saying "In this particular circumstance, having this particular
> method, makes memcpy invalid - ensure you use the member's assign-copy
> operator".
You *still* don't understand. The standard is butting in because its
intent is to enable implementations to invent object models other than
the C object model. That means that non-C objects (i.e. non-PODS) must
be systematically handled differently. The intent isn't merely to
force you to use the assignment operator, it's to enforce agnosticism
about the whole underlying object model.
Now, you may believe that that choice was a bad one, and that the POD
object model is the only one that implementations should have been
allowed to use, just as you believe that the decision to allow trapping
union implementations was a bad one. Very well. You are entitled to
your opinion. But the standards committee, after a fair amount of
thought and debate, decided otherwise, and if you really want to
persuade them to overturn this -- rather foundational -- decision
(rather than just vent your frustration), you're going to have to build
a case that it is really important that they do so.
> The standard should not prevent legitimate code simply to
> increase code safety. C++ is not an educational language.
While we're on the subject, can I just say that not everyone shares
your view that type safety is for newbies, students and amateurs. The
attitude that "type safety is for the weak and foolish" isn't going to
improve your chances of getting what you want.
Although C++ is sacrifices type-safety in some places, it never does so
without reason, and the accepted reasons just about always boil down to
three:
(a) compatibility with C (typically limited to PODs where possible,
because C is limited to PODs, and the C object model applies to PODs)
(b) impossible to enforce typesafety without whole program analysis or
(c) allows you to do something which is both obviously useful and
impossible any other way
I'm not sure that any of the three generic exceptions applies here.
> I think what I want is 'overlay', which co-incidentally happens to be
> identical to an anonymous union that permits non-pod members.
>
> overlay
> {
> type1 name1;
> ..
> typen namen;
> };
>
> An overlay occupies sufficient memory to contain its largest member. No
> initialisation, copying, or destruction of members is performed by default.
> An overlay is not a type, but a storage/alignment instruction. It may only
> appear within a class.
> Use of any overlaid name-x is equivalent to
> reinterpret_cast<type-x&>(overlay). The normal rules of reinterpret_cast
> apply.
> There is no restriction on the members or types within an overlay. If any
> member is volatile in whole or part, the entire overlay is considered
> volatile.
> Appearance of overlay within a class causes that class to be non-POD.
> Use of overlay with a single member is a means of disabling default
> construction/copying/destruction for that member.
This is *much* better, for a couple of reasons. First, it focuses on
what you want, not on why the committee is foolish or paternalistic for
not providing it in the first place. Second, it detaches the rules for
overlaid storage from the rules for unions, and explicitly says that
it's syntactic sugar for raw storage. (Which, I know, is what you
think unions should have been in the first place, but they aren't .)
I'm still doubtful about how such a proposal would be received, though,
simply because how to work with overlaid storage is -- I think --
considered a solved problem in C++. I've show the basic implementation
technique elsewhere in this thread (unions of aligned_storage objects),
and boost::variant illustrates one way in which the technique can be
wrapped up in a reusable class for a more convenient syntax (plus
complete type safety, for free!). Naturally, a solution built directly
into the language might have a somewhat more convenient syntax than a
library-based solution.
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: crosbie@digitalproductions.co.uk ("Crosbie Fitch")
Date: Thu, 8 Jun 2006 19:50:47 GMT Raw View
<kuyper@wizard.net> wrote in message
news:1149782827.305720.255770@i39g2000cwa.googlegroups.com...
> Crosbie Fitch wrote:
> But you said it would have the same effects as the corresponding struct
> you showed, which does involve default constructor calls. I think you
> might want to come up with some way of describing what you want to have
> happen, that doesn't involve that comparison with a struct type.
Please forgive my uncompilable and incomplete examples.
Disregard the other member, thus:
Apart from overlaid storage there should be no difference between:
{ struct S { B b; } s; s.b=B(123); }
and
{ union U { B b; } u; new(&u.b) B(); u.b=B(123); u.b.B::~B(); }
Given:
struct B
{ int a;
B():a(0) { }
B(int i):a(i) {}
B(const B& b):a(b.a) { }
~B() { }
B& operator=(const B& b) { a=b.a; return *this; }
}
> I'm saying that if you allow non-POD types in your union, they either
> run the risk of not having their constructors called, or of having
> constructors called for overlapping objects without an intervening
> destructor call.
Not if the compiler butts out the moment a non-POD member is present in the
union.
It won't call any constructors and will refuse to provide a default
constructor for the enclosing class.
> One of the key advantages of C++ over C is the
> guarantees it provides about automatic construction and destruction of
> objects, and I don't think it's a good idea to extend C unions to allow
> members to have types for which those guarantees are both desireable
> and impossible to provide.
unions should be thought of as overlays, not objects. Thinking of a union as
an object leads you astray - into saying it's an object only as long as what
it contains can be treated as not an object. That's sophistry.
> And for non-POD types, that violates the design of C++.
No it doesn't. If you can't guarantee construction of non-POD members, don't
provide a default constructor in that event. That's better than simply
prohibiting non-POD members. NB I'm only really after this for anonymous
unions anyway.
> Any object with
> a non-trivial constructor should have that constructor called.
You're right.
And that's why, when a union has a member with a non-trivial constructor (a
non-POD class) that union cannot have a default constructor created for it.
Instead, the programmer must do it explicitly, in-place.
However, a union isn't an object, but an overlay - if it has non-POD members
it always needs non-trivial construction - you can't leave the compiler to
create a default constructor for a union that has objects with non-trivial
constructors.
> Using placement new to invoke the constructor is a hack. It's a necessary
> hack, and I wouldn't recommend that it be dropped from the language,
> but every time you use it you should be asking yourself whether there's
> any other way to do what you want; if the answer is yes, it's probably
> a better way of doing that task. Allowing non-POD types in unions only
> becomes workable by use of placement new, or equivalent mechanisms.
Placement new is just as esoteric as using an anonymous union in the first
place. If you're using an anonymous union with non-POD members it's as
esoteric as writing your own memory allocator anyway.
> Could you explain that chain of inference?
I could guess at an explanation, but I doubt it would be particularly
accurate.
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: johnchx2@yahoo.com
Date: Thu, 8 Jun 2006 15:48:46 CST Raw View
Crosbie Fitch wrote:
>
> I am not alone:
>
> http://lists.boost.org/Archives/boost/2002/02/25409.php
>
Quoting from the e-mail referenced above:
The reason this extension is needed is to portably align
memory for both POD and non-POD types using the
usual C idiom of a union of the set of types to align...
This need has been addressed (directly) by the addition of
tr1::aligned_storage and tr1::alignment_of.
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: crosbie@digitalproductions.co.uk ("Crosbie Fitch")
Date: Thu, 8 Jun 2006 22:54:48 GMT Raw View
<johnchx2@yahoo.com> wrote in message
news:1149795442.442566.136140@i39g2000cwa.googlegroups.com...
> This need has been addressed (directly) by the addition of
> tr1::aligned_storage and tr1::alignment_of.
Ok, so he had a different need.
But, it sure looked like he wanted the anonymous union to support non-POD
objects.
He wanted it for the alignment, I want it for the syntax.
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: kuyper@wizard.net
Date: Thu, 8 Jun 2006 18:39:15 CST Raw View
"Crosbie Fitch" wrote:
> <kuyper@wizard.net> wrote in message
> news:1149782827.305720.255770@i39g2000cwa.googlegroups.com...
> > Crosbie Fitch wrote:
> > But you said it would have the same effects as the corresponding struct
> > you showed, which does involve default constructor calls. I think you
> > might want to come up with some way of describing what you want to have
> > happen, that doesn't involve that comparison with a struct type.
>
> Please forgive my uncompilable and incomplete examples.
>
> Disregard the other member, thus:
>
> Apart from overlaid storage there should be no difference between:
> { struct S { B b; } s; s.b=B(123); }
> and
> { union U { B b; } u; new(&u.b) B(); u.b=B(123); u.b.B::~B(); }
>
> Given:
> struct B
> { int a;
> B():a(0) { }
> B(int i):a(i) {}
> B(const B& b):a(b.a) { }
> ~B() { }
> B& operator=(const B& b) { a=b.a; return *this; }
> }
>
>
>
> > I'm saying that if you allow non-POD types in your union, they either
> > run the risk of not having their constructors called, or of having
> > constructors called for overlapping objects without an intervening
> > destructor call.
>
> Not if the compiler butts out the moment a non-POD member is present in the
> union.
> It won't call any constructors and will refuse to provide a default
> constructor for the enclosing class.
That behavior is covered by my comment "they ... run the risk of not
having the constructors called". I don't consider it acceptable if any
union member has a non-trivial constructor.
.
> unions should be thought of as overlays, not objects. Thinking of a union as
> an object leads you astray - into saying it's an object only as long as what
> it contains can be treated as not an object. That's sophistry.
And as such, unions really has no place in C++ - it's a primitive
throwback that makes type safety excessively difficult to achieve. It's
there mainly for backwards compatibility with C.
.
> And that's why, when a union has a member with a non-trivial constructor (a
> non-POD class) that union cannot have a default constructor created for it.
> Instead, the programmer must do it explicitly, in-place.
Which is why it's a bad idea to allow such types in a union.
> Placement new is just as esoteric as using an anonymous union in the first
> place. If you're using an anonymous union with non-POD members it's as
> esoteric as writing your own memory allocator anyway.
Well, that's hardly an endorsement of the idea.
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: crosbie@digitalproductions.co.uk ("Crosbie Fitch")
Date: Thu, 8 Jun 2006 23:40:41 GMT Raw View
Links to similar discussions:
Anonymous Union Catch 22 (2000)
http://tinyurl.com/qr6fk
Unions containing objects w/ constructors/destructors (1995)
http://tinyurl.com/gwjed
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: "Crosbie Fitch" <crosbie@digitalproductions.co.uk>
Date: Thu, 8 Jun 2006 18:46:09 CST Raw View
<johnchx2@yahoo.com> wrote in message
news:1149784035.285474.311920@h76g2000cwa.googlegroups.com...
> You *still* don't understand. The standard is butting in because its
> intent is to enable implementations to invent object models other than
> the C object model. That means that non-C objects (i.e. non-PODS) must
> be systematically handled differently. The intent isn't merely to
> force you to use the assignment operator, it's to enforce agnosticism
> about the whole underlying object model.
Actually, I'm not challenging this at all. What I'm asking for is not in
conflict with the compiler doing what the heck it wants to non-POD objects.
It can represent them as 128bit location sensitive hash codes for all I
care.
I'm suggesting that the union behave in two different ways depending upon
its members.
1) An anonymous union with all POD members
2) An anonymous union with at least one non-POD member
I'm not asking for the first case to be changed at all. It'll be as safe as
it ever was.
The second case can be introduced - with safe ramifications, i.e. that no
default construction/copying/destruction occurs, and that a class containing
such an anonymous union becomes non-POD, and is obliged to perform this
construction/copying/destruction.
Call it 'overlay' if you want. A rose by any other name smells just as
sweet.
>
> Now, you may believe that that choice was a bad one, and that the POD
> object model is the only one that implementations should have been
> allowed to use, just as you believe that the decision to allow trapping
> union implementations was a bad one. Very well. You are entitled to
> your opinion.
I'm not in dispute with this at all. No point in trying to portray my
opposition to this as it simply doesn't exist.
> While we're on the subject, can I just say that not everyone shares
> your view that type safety is for newbies, students and amateurs. The
> attitude that "type safety is for the weak and foolish" isn't going to
> improve your chances of getting what you want.
Again, I've not said this.
I'm against prohibiting non-POD members in unions instead of entrusting the
programmer to manage their lifecycle. The latter can be done safely, just as
writing a smart pointer can be done safely, however, though both are fraught
with risks and gotchas and it is rare to see a flawless implementation,
that's no reason to deny these things to programmers, i.e. on some holy
mission to reduce defects by keeping programmers away from things that might
lead them into temptation to try things not intended for mortal beings.
> Although C++ is sacrifices type-safety in some places, it never does so
> without reason, and the accepted reasons just about always boil down to
> three:
>
> (a) compatibility with C (typically limited to PODs where possible,
> because C is limited to PODs, and the C object model applies to PODs)
>
> (b) impossible to enforce typesafety without whole program analysis or
>
> (c) allows you to do something which is both obviously useful and
> impossible any other way
>
> I'm not sure that any of the three generic exceptions applies here.
C - But whether 'properties' are obviously useful tends to polarise people
into antagonistic camps.
> This is *much* better, for a couple of reasons. First, it focuses on
> what you want, not on why the committee is foolish or paternalistic for
> not providing it in the first place. Second, it detaches the rules for
> overlaid storage from the rules for unions, and explicitly says that
> it's syntactic sugar for raw storage. (Which, I know, is what you
> think unions should have been in the first place, but they aren't .)
Well I contend that that is what they were in the first place, but they've
been shoehorned into an unsafe, unselected, doppelganger unable to lead a
worthwhile independent existence. A bit of a frankenstein's monster.
C++ introduced anonymous unions (A VERY GOOD IDEA), but then hamgstrung them
by hobbling them as named unions were (which I can concede is safe IF
unhobbled anonymous unions can take their place).
> I'm still doubtful about how such a proposal would be received, though,
> simply because how to work with overlaid storage is -- I think --
> considered a solved problem in C++.
That's why there's no reason why an anonymous union with non-POD members
shouldn't also enjoy overlaid storage.
> I've show the basic implementation
> technique elsewhere in this thread (unions of aligned_storage objects),
> and boost::variant illustrates one way in which the technique can be
> wrapped up in a reusable class for a more convenient syntax (plus
> complete type safety, for free!). Naturally, a solution built directly
> into the language might have a somewhat more convenient syntax than a
> library-based solution.
Somehow I don't think the boost::variant is quite the same thing as an
anonymous union. I could allocate memory and use re-interpret cast myself if
I wanted a variant.
struct Complex
{ union
{ float f[2];
M<Complex> magnitude;
};
Complex(float a,b);
};
1) sizeof(Complex) = 2*sizeof(float)
2) M<Complex>::operator float() { return sqrt(...); }
3) M<Complex>::operator=(float f) { sets the magnitude to f };
Notice how magnitude takes up no additional storage and yet provides an
additional read/write property of a complex number?
The two floats could have been magnitude and direction, with x and y pseudo
properties instead. The exposed member variables of the class do not dictate
the implementation (I'd prefer private access modifiers, but hey, can't have
everything).
I can use
Complex c; c.x=123.f; c.magnitude=55.f;
I don't believe the boost::variant can do the same job as the anonymous
union can above. Unless you or anyone else knows differently.
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: "Greg Herlihy" <greghe@pacbell.net>
Date: Fri, 2 Jun 2006 17:57:30 CST Raw View
"Crosbie Fitch" wrote:
> Anonymous union rules against members having constructors & non-default
> assignment seem fairly arbitrary and unnecessary. :-)
>
> template <>
> class X
> {
> union
> { object d;
> element<X,1> d1; // First pseudo member of d
> element<X,2> d2; // Second pseudo member of d
> };
> };
> ..
> X x,y;
>
> x.d1=23; // Works fine element<> has operator=(int)
> x.d2=123; // Fine
> y.d1=5; // Fine too
> y.d2=x.d1; // Fine - converts d2 to int
>
> x.d2=y.d2; // Invokes default assignment operator :-(
>
> The C std in its infinite wisdom says that members of an anonymous union can
> do what the hell they want except they must not provide a default
> constructor or assignment (or require a non-trivial assignment).
>
> I neither want x.d2=y.d2 to do nothing, nor to do default assignment. What
> can I do?
>
> The only solution I've come up with is not really good enough, i.e.
>
> template <>
> class X
> {
> union
> { object d;
> const element<X,1> d1;
> const element<X,2> d2;
> };
> };
>
> By adding const, and constifying the element::operator=(int) with a
> const_cast to undo it, I can oblige a conversion rather than a default
> assignment.
>
> I can suffer the lack of constructors - that's fine. But, prohibiting
> non-default assignment, WHY???
The C++ prohibition against union members having non-trivial
constructors, destructors or assignment operators makes perfect sense.
The reason is simply that it is the program's responsibility - not the
compiler's - to know which one of a union's members is currently
"active" - that is, which of its members was the last one assigned a
value. It should be clear that without this information, the only way
to make a copy a union safely is to require that none of its members
have a custom assignment operator - because the compiler will not know
to call it.
Furthermore, it should be just as clear that no object can be assigned
a value unless the assignment operator for that type is invoked.
Otherwise the consequences are likely to be disasterous. Imagine for
example a union that had a shared pointer (such as a
std::tr1::shared_ptr) as a member. After copying the union (without
invoking shared_ptr's assignment operator), the program would then have
two, identical shared pointers - but each with a use_count of 1. So
when either smart_ptr is destoyed, the other one will be invalidated
prematurely.
As a practical matter, C++ supports unions largely to be compatible
with C - and there is little reason to use them for much else. Because
as the restrictions concerning unions make clear - unions are not type
safe. And C++ usually supports alternatives to unions that are type
safe - inheritance and polymorphic pointers to name just two. And most
programming problems are solved best by thinking in terms of
requirements and not in terms of solutions. So the question is not how
to force a union to support behavior that it is entirely unsuited for -
the question is what exactly does the program have to do - and then to
frame the solution in terms of meeting those requirements.
And if a union is to be used, then the only approach is to eliminate
the non POD types. I would imagine that a union like the one below
could suffice:
int main()
{
union
{
char data[4];
struct
{
char d0;
char d1;
char d2;
char d3;
};
} x, y;
x.d1 = 23; // Works fine
x.d2 = 123; // Fine
y.d1 = 5; // Fine
y.d2 = x.d1; // Fine
x.d2 = y.d2; // Fine
}
Greg
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: cbarron3@ix.netcom.com (Carl Barron)
Date: Sat, 3 Jun 2006 03:16:37 CST Raw View
Greg Herlihy <greghe@pacbell.net> wrote:
> And if a union is to be used, then the only approach is to eliminate
> the non POD types. I would imagine that a union like the one below
> could suffice:
>
> int main()
> {
> union // anon_1
> {
> char data[4];
>
> struct // anon_2
> {
> char d0;
> char d1;
> char d2;
> char d3;
> };
> } x, y;
>
> x.d1 = 23; // Works fine
> x.d2 = 123; // Fine
> y.d1 = 5; // Fine
> y.d2 = x.d1; // Fine
>
> x.d2 = y.d2; // Fine
> }
as long as data is not used for array access of the struct. There is
no guarantee that sizeof(anon_2) == 4 or that offsetof(anon_2,d1)==1,
etc.
the only guarntess are that d0 and data[0] have the same address and
that sizeof(anon_1)>= max(4,sizeof(anon_2)). Any more assumptions is
non standard, and probably compiler option dependent as well...
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: "Greg Herlihy" <greghe@pacbell.net>
Date: Sat, 3 Jun 2006 11:08:16 CST Raw View
Carl Barron wrote:
> Greg Herlihy <greghe@pacbell.net> wrote:
>
> > And if a union is to be used, then the only approach is to eliminate
> > the non POD types. I would imagine that a union like the one below
> > could suffice:
> >
> > int main()
> > {
> > union // anon_1
> > {
> > char data[4];
> >
> > struct // anon_2
> > {
> > char d0;
> > char d1;
> > char d2;
> > char d3;
> > };
> > } x, y;
> >
> > x.d1 = 23; // Works fine
> > x.d2 = 123; // Fine
> > y.d1 = 5; // Fine
> > y.d2 = x.d1; // Fine
> >
> > x.d2 = y.d2; // Fine
> > }
>
> as long as data is not used for array access of the struct. There is
> no guarantee that sizeof(anon_2) == 4 or that offsetof(anon_2,d1)==1,
> etc.
> the only guarntess are that d0 and data[0] have the same address and
> that sizeof(anon_1)>= max(4,sizeof(anon_2)). Any more assumptions is
> non standard, and probably compiler option dependent as well...
With a slightly more elaborate declaration the union can guarantee that
each field in the struct (d1, d2, d3, d4) lines up exactly with the
corresponding element in the array:
#include <tr1/type_traits>
typedef std::tr1::aligned_storage<1, 1>::type Aligner;
union Element
{
Aligner u;
char c;
Element& operator=(char ch)
{
c = ch;
return *this;
}
};
union U
{
Element data[4];
struct
{
Element d1;
Element d2;
Element d3;
Element d4;
};
} x, y;
Greg
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: cbarron3@ix.netcom.com (Carl Barron)
Date: Sat, 3 Jun 2006 21:01:49 GMT Raw View
Greg Herlihy <greghe@pacbell.net> wrote:
> Carl Barron wrote:
> > Greg Herlihy <greghe@pacbell.net> wrote:
> >
> > > And if a union is to be used, then the only approach is to eliminate
> > > the non POD types. I would imagine that a union like the one below
> > > could suffice:
> > >
> > > int main()
> > > {
> > > union // anon_1
> > > {
> > > char data[4];
> > >
> > > struct // anon_2
> > > {
> > > char d0;
> > > char d1;
> > > char d2;
> > > char d3;
> > > };
> > > } x, y;
> > >
> > > x.d1 = 23; // Works fine
> > > x.d2 = 123; // Fine
> > > y.d1 = 5; // Fine
> > > y.d2 = x.d1; // Fine
> > >
> > > x.d2 = y.d2; // Fine
> > > }
> >
> > as long as data is not used for array access of the struct. There is
> > no guarantee that sizeof(anon_2) == 4 or that offsetof(anon_2,d1)==1,
> > etc.
> > the only guarntess are that d0 and data[0] have the same address and
> > that sizeof(anon_1)>= max(4,sizeof(anon_2)). Any more assumptions is
> > non standard, and probably compiler option dependent as well...
>
> With a slightly more elaborate declaration the union can guarantee that
> each field in the struct (d1, d2, d3, d4) lines up exactly with the
> corresponding element in the array:
>
> #include <tr1/type_traits>
>
> typedef std::tr1::aligned_storage<1, 1>::type Aligner;
>
> union Element
> {
> Aligner u;
> char c;
>
> Element& operator=(char ch)
> {
> c = ch;
> return *this;
> }
> };
>
> union U
> {
> Element data[4];
>
> struct
> {
> Element d1;
> Element d2;
> Element d3;
> Element d4;
> };
> } x, y;
>
> Greg
>
I don't think so. This only garauntees [assuming its valid] that the
union can be aligned in any mmory address not there is no internal
padding in a struct containing it.
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: kanze.james@neuf.fr (James Kanze)
Date: Sat, 3 Jun 2006 21:00:56 GMT Raw View
Crosbie Fitch wrote:
> Anonymous union rules against members having constructors &
> non-default assignment seem fairly arbitrary and unnecessary.
> :-)
Not just in anonymous unions. In unions in general. The reason
is simple: how do you know which constructor to call, when? And
how can you implement a non-trivial assignment operator if you
don't know the type of the object you are assigning to?
> template <>
> class X
> {
> union
> { object d;
> element<X,1> d1; // First pseudo member of d
> element<X,2> d2; // Second pseudo member of d
> };
> };
> ..
> X x,y;
> x.d1=3D23; // Works fine element<> has operator=3D(int)
> x.d2=3D123; // Fine
> y.d1=3D5; // Fine too
> y.d2=3Dx.d1; // Fine - converts d2 to int
> x.d2=3Dy.d2; // Invokes default assignment operator :-(
I'm not sure I understand the above. If any element of the
union has a non-trivial constructor, destructor or assignment
operator, the definition of the union is illegal. There is no
question of what gets called; the code simply doesn't compile.
> The C std in its infinite wisdom says that members of an
> anonymous union can do what the hell they want except they
> must not provide a default constructor or assignment (or
> require a non-trivial assignment).
The C standard doesn't allow any of the members to have a
non-trival constructor, destructor or assignment operator
either. In fact, the C standard doesn't allow anything, member
of a union or not, to have a non-trivial constructor, destructor
or assignment operator.
Generally speaking, unions and C++ type classes (i.e. things
that don't make sense in C) don't mix.
--=20
James Kanze kanze.james@neuf.fr
Conseils en informatique orient=E9e objet/
Beratung in objektorientierter Datenverarbeitung
9 place 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.comeaucomputing.com/csc/faq.html ]
Author: "Crosbie Fitch" <crosbie@quidmusic.com>
Date: Mon, 5 Jun 2006 08:38:09 CST Raw View
"James Kanze" <kanze.james@neuf.fr> wrote in message
news:e5sosi$j9o$1@nntp.aioe.org...
>I'm not sure I understand the above. If any element of the
>union has a non-trivial constructor, destructor or assignment
>operator, the definition of the union is illegal. There is no
>question of what gets called; the code simply doesn't compile.
Let's ignore the constructor & destructor - the union has no business
calling them anyway, and to some extent, requiring their absence has little
impact (despite being petty).
You can define ANY assignment operator you like APART from the default copy
assignment.
So given:
class Fred
{
// Fred& operator=(const Fred&); MUST NOT BE DEFINED
};
We can have:
union Busybody
{
Fred f;
};
However, we can do this:
class Jim
{
// Jim& operator=(const Jim&); MUST NOT BE DEFINED
Jim& operator=(const Fred&); // Fine
Jim& operator=(const Jim&) const; // Fine
Jim& operator=(int); // Fine
operator int() const; // Fine
};
union Sillybody
{
Jim j;
};
Jim jim;
jim=123;
Sillybody s;
Busybody b;
s.j=b.f; // Fine
s.j=123; // Fine
s.j=jim; // No good
const Sillbody cs;
cs.j=jim; // Fine
So, I hope you can see why it's extremely irritating to have a class Jim
with potentially umpteen zillion operators and member functions, but where,
due to someone worried about giving metal scissors to kiddies, the class is
prohibited from redefining the assignment-copy operator.
If I can decide what *(Jim*)x=*(const Fred*)y means and what *(const
Jim*)x=*(const Jim*)y means why the hell can't I decide what
*(Jim*)x=*(const Jim*)y means ??
By definition, a union should never even think of calling the assignment
copy operators of its members - it should be blind to its members. No member
should ever be considered copied. The program only ever copies the union's
storage. If such a bitwise copy is unsafe for certain objects contained
within that storage then it's up to the programmer (not the standards
committee) to be aware of this. Otherwise you may as well ban casts and
memcpy too.
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: crosbie@quidmusic.com ("Crosbie Fitch")
Date: Mon, 5 Jun 2006 15:41:03 GMT Raw View
"Greg Herlihy" <greghe@pacbell.net> wrote in message
news:1149286750.439474.151470@i40g2000cwc.googlegroups.com...
> The reason is simply that it is the program's responsibility - not the
> compiler's - to know which one of a union's members is currently
> "active" - that is, which of its members was the last one assigned a
> value. It should be clear that without this information, the only way
> to make a copy a union safely is to require that none of its members
> have a custom assignment operator - because the compiler will not know
> to call it.
Does any compiler today ever actually take advantage of this 'assignment
defines only legitimate readable member' rule?
Since the year dot unions have been used precisely to read a different
member than the one last written to.
Why can't we have unions as straightforward, no nonsense 'shared, coincident
memory'?
The union can be defined to be considered to only ever contain POD, but this
consideration is precisely so that it isn't enforced and enables programmers
to select which representation they wish to interpret this memory area as,
by using the respective member.
Copying a union should be defined as memcpy(&Ulhs,&Urhs,sizeof(U));
Why does the compiler need to start sticking its nose into the programmer's
business and deciding what members should or shouldn't do and how they
should do it?
> Furthermore, it should be just as clear that no object can be assigned
> a value unless the assignment operator for that type is invoked.
> Otherwise the consequences are likely to be disasterous. Imagine for
> example a union that had a shared pointer (such as a
> std::tr1::shared_ptr) as a member. After copying the union (without
> invoking shared_ptr's assignment operator), the program would then have
> two, identical shared pointers - but each with a use_count of 1. So
> when either smart_ptr is destoyed, the other one will be invalidated
> prematurely.
This is nannying the programmer. If they can unwittingly read a smart
pointer when they last wrote a double, they can just as easily read a float
from a union when they last wrote an int.
What's more, compilers already let you store pointers in unions and write
doubles into their storage.
It is patronising to presume that it is magically safer to take decisions
away from the programmer by restricting what objects can or can't do if they
happen to be stored in a union.
>
> As a practical matter, C++ supports unions largely to be compatible
> with C - and there is little reason to use them for much else.
'Little reason', partly because they've been hobbled into plastic scissors
fitting into some Pascal style of programmatic rigour.
> Because as the restrictions concerning unions make clear - unions are not
> type
> safe.
If it's clear, then why go further and hobble their use?
Simply let it stand: 'unions overlay storage'.
They are neither initialised, nor destroyed, they are copied bit for bit.
They may contain any object with any methods.
If any member is volatile, the entire union is volatile.
Management of the members of a union, their initialisation, copying,
destruction, order of access, etc. is entirely the responsibility of the
programmer.
The compiler will make no restrictions concerning the methods of a member,
because no methods will ever be called by default.
A union operates as if casting a void* to the union into to a pointer to the
type of the member, e.g.
union U { int a; float f; } u;
void* v=&u;
u.a === *(int*)v
u.f=4.f === *(float*)v=4.f;
U u2=u === memcpy(&u2,&u,sizeof(U))
U u3(0) === memset(&u3,0,sizeof(U))
U* up=new U === (U*)malloc(sizeof(U))
U* up0=new U(0) === (U*)calloc(1,sizeof(U))
U* up2=new U(u2) === memcpy(malloc(sizeof(U)),&u2,sizeof(U))
>From a C++ perspective, you could naturally extend unions to have similar
methods to any other object, i.e. assignment operators, constructors,
destructors, etc. "A union is like a struct but with all members and bases
having overlaid storage, and default public access". Default copy
constructor and assignment operators are bitwise. 0 is a union with all
memory initialised to zero bits. A union may be cast to bool (false iff all
bits zero).
What's more this change would break nothing, and yet would bring so much
more to the language (especially for generic/template programming).
Anyway, whether unions become C++ified or not, they should at least be able
to contain any member without being fussy as to the methods those members
have - especially given the union should have no business calling any of its
members' methods anyway.
NB Similar to a struct, a union may only be able to contain a reference if
the union has a non-default constructor. However, there's no reason why a
union may not contain an object that has a reference member - given you can
cast a void* to an instance of such an object. If you can entrust the
programmer with a cast, you can entrust them with an undiluted union.
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: johnchx2@yahoo.com
Date: Mon, 5 Jun 2006 10:53:49 CST Raw View
Crosbie Fitch wrote:
> Let's ignore the constructor & destructor - the union has no business
> calling them anyway, and to some extent, requiring their absence has little
> impact (despite being petty).
You seem to be thinking exclusively in terms of operations on
individual union members. Perhaps you have forgotten that it's legal
to construct, copy and assign unions as a whole. For example:
struct A {};
struct B {};
union U {
A a;
B b;
};
void foo( const U& x )
{
U m (x); // 1 copy ctor
U n; // 2 default ctor
n = m; // 3 assignment
} // 4 dtors for m and n
Now, imagine if struct A had a non-trivial copy ctor, non-trivial
default ctor, a non-trivial copy assignment operator, and a non-trivial
destructor. What, exactly, should lines 1, 2, 3 and 4 above do?
> Otherwise you may as well ban casts and memcpy too.
Well, memcpy on non-PODs does result in undefined behavior, for
precisely the same reason. Certain casts also have undefined behavior.
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: "Crosbie Fitch" <crosbie@quidmusic.com>
Date: Mon, 5 Jun 2006 13:26:22 CST Raw View
<johnchx2@yahoo.com> wrote in message
news:1149520622.464471.188470@f6g2000cwb.googlegroups.com...
> struct A {};
> struct B {};
>
> union U {
> A a;
> B b;
> };
>
> void foo( const U& x )
> {
> U m (x); // 1 copy ctor
> U n; // 2 default ctor
> n = m; // 3 assignment
> } // 4 dtors for m and n
>
> Now, imagine if struct A had a non-trivial copy ctor, non-trivial
> default ctor, a non-trivial copy assignment operator, and a non-trivial
> destructor. What, exactly, should lines 1, 2, 3 and 4 above do?
1=== U m; memcpy(&m,&x,sizeof(U));
2=== U n; // no initialisation (no constructor of a or b called)
3=== memcpy(&n,&m,sizeof(U));
4=== destruction equivalent to that for struct X { char m[sizeof(U)]; }
Let's say it was this:
struct S{ A a; B b; };
struct T{ char a[sizeof(A)], b[sizeof(B)]; };
void foo(const S& x)
{ T m( (const T&)x);
T n;
n=m;
}
I can do this in C++ today without the compiler exploding (even if it may
warn me what I'm doing is suspect). What happens at runtime is my own
lookout. This is why the compiler should also butt out when I assign unions.
It should not worry about their members. It's the programmer's lookout to
decide whether they might be exploiting undefined behaviour or not.
The members of S are only constructed by default for the CONVENIENCE of the
programmer, and because a typically useful default is available and
unsurprising. In C, the members of a struct or union were not 'constructed',
so there's no convention to be adhered to here.
The members of U should not be constructed by default, because such
construction is rarely convenient and would often be surprising.
A union's default implementation should look like this:
union U
{
public:
... // Any number of public members having overlaid storage
public:
U() { }
U(const U& u) { memcpy(this,&u,sizeof(U)); }
~U() { }
U& operator=(const U& u) { memcpy(this,&u,sizeof(U)); return *this; }
};
You could add another constructor if you really wanted to that took as its
arg the type of the first member, e.g.
U(const A& aa):a(aa) { }
Anyway, this default implementation of a union makes no requirement of its
members, precisely because it does not recognise them, nor does it need to.
I contend that this interpretation fits better and is truer to C and C++.
>> Otherwise you may as well ban casts and memcpy too.
>
> Well, memcpy on non-PODs does result in undefined behavior, for
> precisely the same reason. Certain casts also have undefined behavior.
Just because something is undefined should not mean that C++ is hobbled in
order to dissuade exploitation of de facto standard behaviour by 99% of
compilers since C was first invented.
You simply say:
"A union holds members of any type overlaid into the same storage. Undefined
behaviour results when any member is read (directly or via a method) that
hasn't been properly initialised by assignment or calling an appropriate
constructor, e.g. an instance of an object with a non-trivial default
constructor or user-defined assign-copy operator. Such initialisation is not
performed by the compiler, and is the responsibility of the programmer, e.g.
via placement new. The compiler may warn when such members are used or when
a member is read without having been initialised - comparable in severity to
the warning given for an old style cast from void*"
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: James Kanze <kanze.james@neuf.fr>
Date: Mon, 5 Jun 2006 13:26:02 CST Raw View
Crosbie Fitch wrote:
> "Greg Herlihy" <greghe@pacbell.net> wrote in message
> news:1149286750.439474.151470@i40g2000cwc.googlegroups.com...
>> The reason is simply that it is the program's responsibility
>> - not the compiler's - to know which one of a union's members
>> is currently "active" - that is, which of its members was the
>> last one assigned a value. It should be clear that without
>> this information, the only way to make a copy a union safely
>> is to require that none of its members have a custom
>> assignment operator - because the compiler will not know to
>> call it.
> Does any compiler today ever actually take advantage of this
> 'assignment defines only legitimate readable member' rule?
Some versions of Microsoft C did -- I haven't verified it
recently.
> Since the year dot unions have been used precisely to read a
> different member than the one last written to.
It was more or less accepted in K&R C. The C standard forbid
it, and even before then, it didn't work with some compilers.
The standard says that the correct way to do this is to use
reinterpret_cast.
> Why can't we have unions as straightforward, no nonsense
> 'shared, coincident memory'?
Because the authors of the the C standard decided otherwise.
[...]
>> Furthermore, it should be just as clear that no object can be
>> assigned a value unless the assignment operator for that type
>> is invoked. Otherwise the consequences are likely to be
>> disasterous. Imagine for example a union that had a shared
>> pointer (such as a std::tr1::shared_ptr) as a member. After
>> copying the union (without invoking shared_ptr's assignment
>> operator), the program would then have two, identical shared
>> pointers - but each with a use_count of 1. So when either
>> smart_ptr is destoyed, the other one will be invalidated
>> prematurely.
> This is nannying the programmer. If they can unwittingly read
> a smart pointer when they last wrote a double, they can just
> as easily read a float What's more, compilers already let you
> store pointers in unions and write doubles into their storage.
Smart pointers (or any type with a user defined assignment
operator=) are a different problem. If you take something like
boost::shared_ptr, the assignment operator will access the old
value supposing it was also a boost::shared_ptr ... and probably
core dump doing so. Types with user defined assignment and
construction really don't work in non-discriminated unions.
Arguably, the problem with unions as they are now specified is
that they try to do two jobs: that of a discriminated union, and
that of a low level memory overlay. With the results that the
rules which allow the compiler to implement discrimination
prevent effective use as a low level memory overlay, and by not
requiring the discrimination, they are forced into rules which
don't allow effective use as a discriminated union.
--
James Kanze kanze.james@neuf.fr
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.comeaucomputing.com/csc/faq.html ]
Author: crosbie@quidmusic.com ("Crosbie Fitch")
Date: Mon, 5 Jun 2006 19:42:44 GMT Raw View
"James Kanze" <kanze.james@neuf.fr> wrote in message
news:e61mab$32n$1@nntp.aioe.org...
> Smart pointers (or any type with a user defined assignment
> operator=) are a different problem. If you take something like
> boost::shared_ptr, the assignment operator will access the old
> value supposing it was also a boost::shared_ptr ... and probably
> core dump doing so. Types with user defined assignment and
> construction really don't work in non-discriminated unions.
They work just fine. They work as well as void* members overlaid with
doubles and char arrays. In all cases the programmer MUST cater for their
initialisation, assignment, and destruction BECAUSE the union is unable to
do it for them.
class Twin
{
enum { fixed, unlimited_precision } w;
union
{ int n;
smart_ptr<unlimited> p;
};
Twin::Twin()
: w(fixed)
{ n=0;
}
Twin& operator+=(const Twin& t)
{
...
int m=n;
...
w=unlimited_precision;
new(&p) smart_ptr<unlimited>(new unlimited(m));
...
return *this;
}
~Twin()
{ if (w==unlimited_precision)
(&p)->smart_ptr<unlimited>::~smart_ptr<unlimited>();
}
}
If some coders forget that Twin also needs a non-default copy constructor
and assign-copy operator then that's their mistake to learn from. The
language should not try to help them out by hobbling the language for coders
who have learnt.
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: johnchx2@yahoo.com
Date: Mon, 5 Jun 2006 21:35:46 CST Raw View
Crosbie Fitch wrote:
> 1=== U m; memcpy(&m,&x,sizeof(U));
> 2=== U n; // no initialisation (no constructor of a or b called)
> 3=== memcpy(&n,&m,sizeof(U));
> 4=== destruction equivalent to that for struct X { char m[sizeof(U)]; }
I see...I hadn't realized that you were proposing a wholesale rewrite
of the semantics of unions. Basically, the proposal seems to boil down
to "treat all members of unions as if they were POD whether they are
POD or not." I'm having trouble thinking of a non-trivial way to use
such a construct that (a) isn't better accomplished with
tr1::aligned_storage or (b) doesn't give rise to undefined behavior.
I understand that you don't mind writing code with undefined behavior
(though I'm not sure I understand why), but I think persuading the
committee that they ought to make writing such code more convenient
will prove difficult.
Actually, it occus to me that you can get more or less the behavior you
want simply by telling the compiler the truth...namely by declaring the
union with with members of tr1::aligned_storage type, instead of with
the non-POD object type.
using std::tr1::aligned_storage;
using std::tr1::alignment_of;
typedef aligned_storage< sizeof(A), alignment_of<A> > A_Storage;
union U {
A_Storage a;
B b;
};
You'd have to use placement new to initialize any non-POD members, but
you need to do that anyway (I can't see any other way to guarantee the
proper initialization of a non-POD member of a union in your
memcpy-like approach). You'd also need to use casts to access the
members as constructed objects, but that's just telling the truth
again, and you don't seem to mind casts.
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: "kanze" <kanze@gabi-soft.fr>
Date: Tue, 6 Jun 2006 10:47:09 CST Raw View
"Crosbie Fitch" wrote:
> "James Kanze" <kanze.james@neuf.fr> wrote in message
> news:e61mab$32n$1@nntp.aioe.org...
> > Smart pointers (or any type with a user defined assignment
> > operator=) are a different problem. If you take something
> > like boost::shared_ptr, the assignment operator will access
> > the old value supposing it was also a boost::shared_ptr ...
> > and probably core dump doing so. Types with user defined
> > assignment and construction really don't work in
> > non-discriminated unions.
> They work just fine. They work as well as void* members
> overlaid with doubles and char arrays. In all cases the
> programmer MUST cater for their initialisation, assignment,
> and destruction BECAUSE the union is unable to do it for them.
In other words, given:
union U { A a ; B b ; } ;
where A and B both have non-trivial destructors, constructors
and assignment operators, if I want to assign to a:
1. I have to know what type is currently assigned to the union.
2. If it is a, I use the assignment operator,
3. Otherwise, I explicity call a.~A(), then use placement new
to construct the b.
And what, my I ask, happens when my union object goes out of
scope?
But you're just kidding, right?
--
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.comeaucomputing.com/csc/faq.html ]
Author: "Crosbie Fitch" <crosbie@digitalproductions.co.uk>
Date: Tue, 6 Jun 2006 13:26:33 CST Raw View
"kanze" <kanze@gabi-soft.fr> wrote in message
news:1149608342.834520.232820@y43g2000cwc.googlegroups.com...
> In other words, given:
>
> union U { A a ; B b ; } ;
>
> where A and B both have non-trivial destructors, constructors
> and assignment operators, if I want to assign to a:
>
> 1. I have to know what type is currently assigned to the union.
> 2. If it is a, I use the assignment operator,
> 3. Otherwise, I explicity call a.~A(), then use placement new
> to construct the b.
Of course.
You seem to be implying that such knowledge is a burden...
Where does it say that a union must be a data structure suitable for use by
beginners who haven't got a clue why or how to use them?
> And what, my I ask, happens when my union object goes out of
> scope?
The union is destroyed (which, by default, does NOT call its members'
destructors).
> But you're just kidding, right?
Absolutely not.
If there's a joke anywhere, it's that a member of a union must have a
default assign-copy constructor.
Remove that quite unnecessary restriction and replace it with a warning,
e.g. "Warning: member x has a non-default assign-copy operator. Unions are
copied bitwise by default and this may be incompatible with
X::operator=(const X&)."
"Warning: member X has a user defined or non-trivial default constructor. A
union's members are not initialised by default and so this must be performed
explicitly prior to use of the member."
"Warning: member X has a user defined or non-trivial default destructor. A
union's members are not destroyed by default and so this must be performed
explicitly prior to use of an alternate member or destruction of the union."
"Warning: you are using a union. Are you sure you know what you're doing?
They are extremely dangerous data structures and require incredibly advanced
knowledge of C++ before countenancing their use. You will need to retain a
record of which member is in use at any time, manually assign or construct
members before use, and destroy afterwards. You must be aware when copying a
union that this only copies the memory and does not call any members'
assignment-copy operators. Using or reading a member that hasn't been
properly initialised is undefined. If in doubt, do not use a union!"
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: crosbie@quidmusic.com ("Crosbie Fitch")
Date: Tue, 6 Jun 2006 18:54:01 GMT Raw View
<johnchx2@yahoo.com> wrote in message
news:1149552168.378163.198510@h76g2000cwa.googlegroups.com...
> I see...I hadn't realized that you were proposing a wholesale rewrite
> of the semantics of unions. Basically, the proposal seems to boil down
> to "treat all members of unions as if they were POD whether they are
> POD or not."
Not at all.
Treat all members of unions like members of any other class, recognised
precisely for what they are.
Apart from overlaid storage there should be no difference between:
struct S { A a; B b; } s; s.b=B(123);
and
union U { A a; B b; } u; new(&u.b) B(); u.b=B(123);
and no restrictions on the members of A or B,
EXCEPT that:
The members of U are not initialised, copied or destroyed by default (unless
the language provides a way to override that default). The union is
considered independently from its members, so the union is copied bitwise by
default (potentially overriden come the glorious day when unions are
upgraded to class status).
It boils down to the union not being treated like a struct. Rather than try
and shoehorn the C concept of a union into some whacky OO doppelganger
device (which has not been achieved), leave it as it was in C, i.e. overlaid
memory. The worst thing to do is to blunt the sharp edges off and make it a
vestigal C curiousity for beginners and almost useless for experts.
If you really wanted to be symmetrical about it you should probably have a
way of explicitly requesting that members of a class not be initialised or
destroyed by default, e.g. S():~a,b(123) { }. At least you can already
override their default copying.
>You'd also need to use casts to access the
> members as constructed objects, but that's just telling the truth
> again, and you don't seem to mind casts.
I do indeed mind casts. I've used them only to show how a union becomes a
far
more convenient equivalent.
The whole point of unions (like structs) is a syntactical
convenience. Otherwise you might as well use mallocs, pointers and casts
throughout.
Remember, I'm not asking for the language to be extended. I'm asking for the
hobbles to be removed, i.e. the unnecessary restrictions on union members.
unions are copied by memcpy anyway. To imagine that actually, each member's
assignment-copy operator is called is a delusion invented in the name of
semantic purity by those who could conceive of little use for a union in C++
and regarded it as a dangerous anachronism to be deprecated.
Those who want a safe language should use C#. I see no reason why sharp
knives should be removed from C++ or blunted just because they present a
risk to beginners. Pointers are a risk. Casts are a risk. Unions are a risk.
By all means provide warnings (as for assignments within expressions), but
for god's sake please permit their unadulterated use.
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: kuyper@wizard.net
Date: Tue, 6 Jun 2006 16:13:24 CST Raw View
"Crosbie Fitch" wrote:
> <johnchx2@yahoo.com> wrote in message
> news:1149552168.378163.198510@h76g2000cwa.googlegroups.com...
> > I see...I hadn't realized that you were proposing a wholesale rewrite
> > of the semantics of unions. Basically, the proposal seems to boil down
> > to "treat all members of unions as if they were POD whether they are
> > POD or not."
>
> Not at all.
>
> Treat all members of unions like members of any other class, recognised
> precisely for what they are.
>
> Apart from overlaid storage there should be no difference between:
> struct S { A a; B b; } s; s.b=B(123);
> and
> union U { A a; B b; } u; new(&u.b) B(); u.b=B(123);
So a default constructor would be mandatory?
> and no restrictions on the members of A or B,
>
> EXCEPT that:
>
> The members of U are not initialised, copied or destroyed by default (unless
> the language provides a way to override that default). The union is
> considered independently from its members, so the union is copied bitwise by
> default (potentially overriden come the glorious day when unions are
> upgraded to class status).
You say that you're not assuming that the members are PODs. That means
(among other things) that they won't necessarily survive a bit-wise
copy. Despite this fact, the union itself is to be copied bitwise.
Could you explain in a little more detail precisely how that's supposed
to work?
> Remember, I'm not asking for the language to be extended. I'm asking for the
> hobbles to be removed, i.e. the unnecessary restrictions on union members.
> unions are copied by memcpy anyway. To imagine that actually, each member's
> assignment-copy operator is called is a delusion invented in the name of
> semantic purity by those who could conceive of little use for a union in C++
> and regarded it as a dangerous anachronism to be deprecated.
I'm curious - who expressed that delusion? That sounds like a
description of the implicitly-defined copy assignment operator for
class types, but it doesn't describe unions.
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: johnchx2@yahoo.com
Date: Tue, 6 Jun 2006 18:25:51 CST Raw View
kuyper@wizard.net wrote:
> "Crosbie Fitch" wrote:
> > Remember, I'm not asking for the language to be extended. I'm asking for the
> > hobbles to be removed, i.e. the unnecessary restrictions on union members.
> > unions are copied by memcpy anyway. To imagine that actually, each member's
> > assignment-copy operator is called is a delusion invented in the name of
> > semantic purity by those who could conceive of little use for a union in C++
> > and regarded it as a dangerous anachronism to be deprecated.
>
> I'm curious - who expressed that delusion? That sounds like a
> description of the implicitly-defined copy assignment operator for
> class types, but it doesn't describe unions.
Actually, unions *are* class types.
I've always been uncomfortable with the implicitly defined copy ctor
and copy assignment operators for unions: member-wise copying and
assignment would seem to imply accessing each member in turn, which is
(a) undefined since it accesses members other than the one "active"
member and (b) always leaves the last declared member in the target
union as the "active" one, which is surprising.
But it's basically moot because the union members are POD, and
everything works out OK under the as-if rule.
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: johnchx2@yahoo.com
Date: Tue, 6 Jun 2006 18:21:03 CST Raw View
"Crosbie Fitch" wrote:
> Rather than try
> and shoehorn the C concept of a union into some whacky OO doppelganger
> device (which has not been achieved), leave it as it was in C, i.e. overlaid
> memory.
But the current language leaves unions EXACTLY as they were in C.
There is no valid C union that isn't also perfectly acceptable in C++.
It's you who are trying to intrude non-C-ish constructs into the C-ish
world of unions.
I'm starting to suspect that you may not be fully grasping how
fundamental the difference is in C++ between PODs and non-PODs. The
sorts of "safety-features-off" bit-twiddling that you're describing
only makes any sense at all with PODs. This is because the standard
guarantees you very little with respect to the binary layout of non-POD
objects.
offestof doesn't work on non-PODs. memcpy doesn't work on non-PODs.
This is because they aren't guaranteed to be laid out anything like you
think they will be. (There are some who claim that they're not
required to be contiguous blocks of memory, but I'm not 100% convinced
of this.) The compiler can add hidden members, and do other tricky
things behind your back. Manipulating a non-POD as if it were POD can
lead to all kinds of surprises, and is never a good idea.
And all it takes to trigger all of these changes is for you to add a
custom copy assignment operator to a class. Adding that one member
function can completely change the binary layout of the class, in ways
that are entirely up to the implementation.
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: James Dennett <jdennett@acm.org>
Date: Wed, 7 Jun 2006 09:45:46 CST Raw View
Crosbie Fitch wrote:
> "kanze" <kanze@gabi-soft.fr> wrote in message
> news:1149608342.834520.232820@y43g2000cwc.googlegroups.com...
>> In other words, given:
>>
>> union U { A a ; B b ; } ;
>>
>> where A and B both have non-trivial destructors, constructors
>> and assignment operators, if I want to assign to a:
>>
>> 1. I have to know what type is currently assigned to the union.
>> 2. If it is a, I use the assignment operator,
>> 3. Otherwise, I explicity call a.~A(), then use placement new
>> to construct the b.
>
> Of course.
>
> You seem to be implying that such knowledge is a burden...
On correctness, yes. Even with relatively strong
programmers, requiring this knowledge would lead
to more defects.
> Where does it say that a union must be a data structure
> suitable for use by
> beginners who haven't got a clue why or how to use them?
I don't believe anyone has advocated such a position here.
However, that doesn't mean that we should not try to make
facilities safe to use by typical C++ users.
>> And what, my I ask, happens when my union object goes out of
>> scope?
>
> The union is destroyed (which, by default, does NOT call its members'
> destructors).
This may be fine by you, as you don't appear to value the
C++ object model as it exists today, where the language
tries to provide guarantees on the lifecycle of objects
(particularly non-PODs).
>> But you're just kidding, right?
>
> Absolutely not.
>
> If there's a joke anywhere, it's that a member of a union must have a
> default assign-copy constructor.
(I'm assuming that's a reference to the copy constructor
and/or the copy assignment operator.)
> Remove that quite unnecessary restriction and replace it with a warning,
> e.g. "Warning: member x has a non-default assign-copy operator. Unions are
> copied bitwise by default and this may be incompatible with
> X::operator=(const X&)."
>
> "Warning: member X has a user defined or non-trivial default constructor. A
> union's members are not initialised by default and so this must be performed
> explicitly prior to use of the member."
>
> "Warning: member X has a user defined or non-trivial default destructor. A
> union's members are not destroyed by default and so this must be performed
> explicitly prior to use of an alternate member or destruction of the union."
>
> "Warning: you are using a union. Are you sure you know what you're doing?
> They are extremely dangerous data structures and require incredibly advanced
> knowledge of C++ before countenancing their use. You will need to retain a
> record of which member is in use at any time, manually assign or construct
> members before use, and destroy afterwards. You must be aware when copying a
> union that this only copies the memory and does not call any members'
> assignment-copy operators. Using or reading a member that hasn't been
> properly initialised is undefined. If in doubt, do not use a union!"
The C++ standard does not mandate *any* "warnings". Only
diagnostics. And when it requires a diagnostic, it never
requires successful translation.
You seem to be coming from a place where you don't care so
much for the protection C++ attempts to afford to its object
model. I don't expect you to get a lot of support for the
way you're currently presenting this; the restrictions on
placing types with special semantics into unions was made
a long time ago, and generally has been seen to be a
reasonable decision.
There are *some* cases with low-level code where it would
be convenient to drop the restrictions, but many of those
are covered by aligned_storage, and the rest can be managed.
It's not appropriate to remove safeguards that protect most
users of C++ (not just novices, who tend not to use unions
in any case) to suit the remaining uses. We want to aim to
reduce, not increase, the amount of undefined behavior in
C++.
-- James
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: crosbie@digitalproductions.co.uk ("Crosbie Fitch")
Date: Wed, 7 Jun 2006 15:03:06 GMT Raw View
<kuyper@wizard.net> wrote in message
news:1149623054.476991.128910@h76g2000cwa.googlegroups.com...
> "Crosbie Fitch" wrote:
>> Apart from overlaid storage there should be no difference between:
>> struct S { A a; B b; } s; s.b=B(123);
>> and
>> union U { A a; B b; } u; new(&u.b) B(); u.b=B(123);
>
> So a default constructor would be mandatory?
No. There would be no restrictions on what methods a member had or did not
have.
A union should have no default constructor or destructor.
A union should be thought of as an area of memory with size large enough to
contain its largest member. Accessing any member of a union should be
thought of as casting a pointer to this area to a pointer of the type of the
member.
Thus given A & B.
struct Union { char m[max(sizeof(A),sizeof(B))]; } s; // union { A a; B
b; } u;
u.a === *(A*)&s;
u.b === *(B*)&s;
> You say that you're not assuming that the members are PODs. That means
> (among other things) that they won't necessarily survive a bit-wise
> copy.
Just as they wouldn't necessarily survive a reinterpret cast.
Until unions are permitted to have user defined methods like classes, the
defaults have to be the C defaults, i.e. no construction/destruction and
bitwise assign-copy (of the memory - not the members).
Remember that unions are esoteric in the first place and even in C often
needed external logic to interpret and manage their contents. There's every
reason to expect that in containing objects those objects will also need
external logic to manage their lifecycle. To instead require that any member
of a union must be treatable as a POD struct, but can otherwise have any
member functions, is unnecessarily blunting the knife for no good reason.
If anyone uses a reinterpret_cast or a union it should be a given that they
know precisely what they're doing, i.e. that "Thou shalt read no member that
thee hath not made good afore".
It is incredibly irksome they now require compilers to prevent (not just
warn) against having members with a particular member function (a
particularly useful one too).
Has no-one considered that a class may be designed precisely with a view to
its appearance within a union?
It is possible that a user defined assign-copy operator may be needed
precisely in order to avoid undefined behaviour.
> Despite this fact, the union itself is to be copied bitwise.
> Could you explain in a little more detail precisely how that's supposed
> to work?
The union is large enough to contain the representation of its largest
member.
Copying a union bitwise by default is just as dangerous as a default
assign-copy operator for a class (with member pointers).
You've got to stop worrying about putting cotton wool on things. C++ is not
Smalltalk or Java.
By trying to make a union slightly safer it has become emasculated.
> I'm curious - who expressed that delusion? That sounds like a
> description of the implicitly-defined copy assignment operator for
> class types, but it doesn't describe unions.
Are you really telling me that you believe that when copying a union each
member's default assign-copy operator is called?
I think you'll find that it's a simple bitwise copy of the union (with no
regard to the members).
"Oooh. But, what if someone has a pointer as a member? It might not be
valid to copy the pointer?"
"Well, if they don't know what they're doing they'll hurt themselves."
"Oooh. But, what if someone has a member object with a user-defined
assign-copy operator?"
"Ah. Yes. They're obviously clueless and we must put a stop to it before
they hurt themselves. It is our duty to make C++ a safer language than C"
So, now I have to go through the hoops:
class A
{
A& CustomAssignCopy(const A& a) { /*copy*/ return *this; }
public:
A& operator=(const A& a) const { return
const_cast<A*>(this)->CustomAssignCopy(a); }
};
union U
{
A a;
};
THAT IS PERMITTED and compilers quite happily compile it, the computer
doesn't burst into flames, the programmer doesn't burst a blood vessel and
the world doesn't end. But, it's a nasty hack and needs further hacks to
restore the const behaviour to const instances.
But, some hand-wringing goody-goody who didn't see significant use for
objects within unions in C++ put a spanner in the works in the name of
health and safety and said "Oooh no, because we can't see why unions should
be used for anything except POD we will make it a right pain for anyone who
tries - because anyone who tries probably doesn't know what they're doing".
In my case I'm not even wanting to exploit undefined behaviour. I need a
custom assign-copy operator precisely in order to ensure the correct
representation within the union is being assigned to.
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: crosbie@digitalproductions.co.uk ("Crosbie Fitch")
Date: Wed, 7 Jun 2006 15:03:52 GMT Raw View
<johnchx2@yahoo.com> wrote in message
news:1149633364.561734.56320@i39g2000cwa.googlegroups.com...
> memcpy doesn't work on non-PODs.
> This is because they aren't guaranteed to be laid out anything like you
> think they will be.
'memcpy doesn't work on non-PODs' !!!
Wow! That's bad news.
So, let's say I had this:
class A {... }* a;
foo()
{void* p=malloc(sizeof(A));
new(p) A();
a=(A*)malloc(sizeof(A));
memcpy(a,p,sizeof(A)); // Doesn't work if A is non-POD?
free(p);
a->method();
delete a;
}
If memcpy doesn't work on non-POD objects then something truly fundamental
has changed, e.g. C++ has become C#.
I don't care how non-POD objects are laid out, because I don't intend to
exploit their layout. I only require that I have the option that I may
overlay their storage with other data, AND that I be given credit for
knowing that the non-POD objects may require construction with placement
new, may require custom non-default assignment, and may require destruction.
All that's required is changing a compiler error to a warning:
"Error: Union member object must have default assign-copy operator"
to
"Warning: Union member object does not have default assign-copy operator"
That's all. It's such a weeny little thing.
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: "Bo Persson" <bop@gmb.dk>
Date: Wed, 7 Jun 2006 10:39:30 CST Raw View
""Crosbie Fitch"" <crosbie@digitalproductions.co.uk> skrev i
meddelandet news:nrGdndeyd-B6ORvZnZ2dnUVZ8s6dnZ2d@bt.com...
> <johnchx2@yahoo.com> wrote in message
> news:1149633364.561734.56320@i39g2000cwa.googlegroups.com...
>> memcpy doesn't work on non-PODs.
>> This is because they aren't guaranteed to be laid out anything like
>> you
>> think they will be.
>
> 'memcpy doesn't work on non-PODs' !!!
>
> Wow! That's bad news.
>
How would it work?!
Try this one:
class nonPOD
{
public:
nonPOD() : ref(&y)
{ }
int gety() const
{ return ref; }
private:
int y;
int& ref;
nonPOD(const nonPOD&);
nonPOD& operator=(const nonPOD&);
};
This class can be constructed, but it cannot be copied or assigned
(because the copy constructor and assignment operator are private).
Do you think it would be a good idea to allow memcpy to make copies?
How would the gety() function work if the class object is
moved/copied?
> So, let's say I had this:
>
> class A {... }* a;
>
> foo()
> {void* p=malloc(sizeof(A));
>
> new(p) A();
>
> a=(A*)malloc(sizeof(A));
>
> memcpy(a,p,sizeof(A)); // Doesn't work if A is non-POD?
No. What if class A contains a pointer to some other data. Will that
data be deleted once, twice, or not at all?
>
> free(p);
>
> a->method();
>
> delete a;
> }
>
>
> That's all. It's such a weeny little thing.
No, it's not. It is a fundamental change!
Bo Persson
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: James Dennett <jdennett@acm.org>
Date: Wed, 7 Jun 2006 11:09:16 CST Raw View
Crosbie Fitch wrote:
> <johnchx2@yahoo.com> wrote in message
> news:1149633364.561734.56320@i39g2000cwa.googlegroups.com...
>> memcpy doesn't work on non-PODs.
>> This is because they aren't guaranteed to be laid out anything like you
>> think they will be.
>
> 'memcpy doesn't work on non-PODs' !!!
>
> Wow! That's bad news.
>
> So, let's say I had this:
>
> class A {... }* a;
>
> foo()
> {void* p=malloc(sizeof(A));
>
> new(p) A();
>
> a=(A*)malloc(sizeof(A));
>
> memcpy(a,p,sizeof(A)); // Doesn't work if A is non-POD?
>
> free(p);
>
> a->method();
>
> delete a;
> }
>
>
> If memcpy doesn't work on non-POD objects then something truly fundamental
> has changed, e.g. C++ has become C#.
It is not valid to use memcpy on non-PODs. If you
choose to do it, you cannot portably cast the resulting
memory to the non-POD type and expect to use it.
I think that it's legal for an implementation to add
a hidden "my address" member to a non-POD object, and
to validate that it's consistent with the object's
address on each access to the object.
This is nothing new, and C++ still isn't C#. It's not
C either; C++'s object model is layered on top of C's,
but non-PODs are a whole world of their own.
-- James
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: johnchx2@yahoo.com
Date: Wed, 7 Jun 2006 11:08:38 CST Raw View
"Crosbie Fitch" wrote:
>
> 'memcpy doesn't work on non-PODs' !!!
>
> Wow! That's bad news.
>
> So, let's say I had this:
>
> class A {... }* a;
>
> foo()
> {void* p=malloc(sizeof(A));
>
> new(p) A();
>
> a=(A*)malloc(sizeof(A));
>
> memcpy(a,p,sizeof(A)); // Doesn't work if A is non-POD?
>
> free(p);
>
> a->method();
>
> delete a;
> }
>
The line "a->method();" has undefined behavior. There is no guarantee
that a points to a valid object of class A. (Assuming class A is
non-POD.)
I suppose it depends on your definintion of "work" of course. memcpy
will indeed copy some bytes. But there's no guarantee that the result
is useable.
>
> If memcpy doesn't work on non-POD objects then something truly fundamental
> has changed, e.g. C++ has become C#.
>
Something fundamental has changed: C++ isn't C, and non-POD types
aren't required to behave like C types. This happened a long time ago,
long before there was C#.
> I don't care how non-POD objects are laid out, because I don't intend to
> exploit their layout.
Assuming that a bitwise copy of an object is a valid object IS
exploiting the layout.
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: crosbie@digitalproductions.co.uk ("Crosbie Fitch")
Date: Wed, 7 Jun 2006 16:10:19 GMT Raw View
<johnchx2@yahoo.com> wrote in message
news:1149634178.807134.42510@u72g2000cwu.googlegroups.com...
> kuyper@wizard.net wrote:
> Actually, unions *are* class types.
Ok, let's be more precise: unions may not have user defined methods. That
pretty much disqualifies them from class status in the usual sense of the
term.
Again, a pretty strange decision given the existence of anonymous unions,
e.g.
class PseudonymousUnion
{
union
{ A a;
B b;
};
..
int AMethod() { return a.method(); }
};
Why not:
union PU
{ A a;
B b;
..
int AMethod() { return a.method(); }
};
It all comes down to a lack of foresight in considering that unions may
actually have some utility - that unions COULD be designed in full knowledge
that their container/user would be aware of which representation/method was
valid. Moreover that they could contain objects that would be designed to be
contained within a union. Especially given the advent of templates that can
inject this knowledge into the union's implementation or that of its member
objects' classes..
> I've always been uncomfortable with the implicitly defined copy ctor
> and copy assignment operators for unions: member-wise copying and
> assignment would seem to imply accessing each member in turn, which is
> (a) undefined since it accesses members other than the one "active"
> member and (b) always leaves the last declared member in the target
> union as the "active" one, which is surprising.
Quite. The emperor is naked. It's a memcpy. The pretence that each member's
assign-copy operator is called simultaneously is part of the unhelpful
delusion that prohibits non-default assign-copy.
> But it's basically moot because the union members are POD, and
> everything works out OK under the as-if rule.
The standards committee couldn't conceive of any practical/safe use for
non-POD union members so they decided an edict that unions should be treated
as only containing POD members was a safe one - with the unfortunate
consequence that this turned into a stipulation, and then that prohibitions
were made to re-inforce it.
Such prohibitions had never been made to prevent out of order access to
union members, so they shouldn't have been made to prevent objects with
non-default assign-copy operators being members.
The rule that reading from uninitialised union members is undefined is
sufficient - along with a warning that a union's notional default
assign-copy operator is a memcpy.
Incidentally, given union { int a,b; } I'd suggest that it's not so much the
order in which members are read or initialised that's important, it's that a
read member is congruent in memory with an initialised representation of the
same type (subject to whether such congruence can be known by the
programmer). Moreover, the methods of some object members may not read any
member, or they may read a different member that the method of the member
via which they were invoked.
Thus
union U
{ float f;
A a;
} u;
u.a=123; // may actually invoke A::A(int) and A::operator=(const A&) which
may assign 123.f to u.f.
And all this is prevented by a weeny little standards compliance test the
compiler has been obliged to do.
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: crosbie@digitalproductions.co.uk ("Crosbie Fitch")
Date: Wed, 7 Jun 2006 17:02:33 GMT Raw View
"Bo Persson" <bop@gmb.dk> wrote in message
news:4eo9kjF1f86otU1@individual.net...
> How would it work?!
> class nonPOD
..
> This class can be constructed, but it cannot be copied or assigned
> (because the copy constructor and assignment operator are private).
Let's not get confused by copying an object and copying the storage
representing the object.
I'm not saying that memcpy can safely copy any object, I'm saying it can
copy the memory representing a non-POD object, i.e. one with a non-default
assign-copy operator. Just because it has a user defined assign-copy
operator doesn't suddenly make it unsafe to memcpy.
It can only safely copy an object if the person using memcpy knows that a
bitwise copy is a safe operation despite the object having a non-default
assign-copy operator.
There are obviously plenty of objects for which memcpy results in an invalid
object, e.g. the object may contain a pointer to itself (a member of a
linked list, say), however, it still copies the storage representing that
object - and if put back at the original address should recreate it ok
(unless it had volatile members, etc.).
Unfortunately, the definition of POD objects, whilst always safe to memcpy,
exclude many objects with non-default assign-copy operators that are also
safe to memcpy and/or safe to exist as members of a union.
Because unions are so ill-suited to use as independent data structures, the
standard seems to have attempted to make them as safe as possible as
independent data structures.
However, perhaps an exception can be made for ANONYMOUS unions? (getting
back to the subject of my post)
Anonymous unions cannot exist independently ouside an encapsulating class
and must exist within a class that supposedly has full knowledge of the
members' construction, copying, and destruction requirements.
Therefore although an independent union must use memcpy and could therefore
preclude non-POD members out of safety considerations, a dependent,
anonymous union should be able to have non-POD members given the containing
class is aware of them, e.g.
class Dub // IKnowWhatMyUnionContains
{
bool bIsDouble;
union
{ double d;
smart_ptr<BigDub> g;
};
public:
... operator=(const Dub& b) { ... /*if both smart*/ g=b.g; return
*this; }
};
One can say that Dub instantly becomes non-Pod the moment it contains a
union, and no default assign-copy operator is generated by default. This
prevents the unwitting copying of the anonymous union since it has no name
with which to be assigned.
So, I'd suggest allowing anonymous unions to have non-POD members was as
safe as allowing them to contain pointers given they need programmer
assistance to be copied.
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: crosbie@digitalproductions.co.uk ("Crosbie Fitch")
Date: Wed, 7 Jun 2006 18:02:02 GMT Raw View
<johnchx2@yahoo.com> wrote in message
news:1149695391.355185.314920@j55g2000cwa.googlegroups.com...
> The line "a->method();" has undefined behavior. There is no guarantee
> that a points to a valid object of class A. (Assuming class A is
> non-POD.)
However, if I know that class A is POD, and only becomes non-POD due to the
presence of a non-default assign-copy operator, then memcpy can copy that
object and I can invoke methods on the copy.
Anyway, this is a tangent. I just wanted to point out that simply being
non-POD does not prevent memcpy creating a valid copy. It also has to be an
object for which memcpy is unsafe (or inherently invalid).
The set of objects for which memcpy always results in a valid object is
larger than the set of POD objects.
> I suppose it depends on your definintion of "work" of course. memcpy
> will indeed copy some bytes. But there's no guarantee that the result
> is useable.
Indeed. It requires knowledge of the class being copied.
>> I don't care how non-POD objects are laid out, because I don't intend to
>> exploit their layout.
>
> Assuming that a bitwise copy of an object is a valid object IS
> exploiting the layout.
My issue is becoming confused here.
2) If one has knowledge of the non-POD class being memcpy'ed, and one knows
that memcpy is safe for it, then memcpy can copy a non-POD object. That is
not an assumption, but a precondition.
This is an academic tangent unrelated to my issue. I don't think there's
really any disagreement here.
1a) Until the union assign-copy operator can be overloaded, it is best
thought of as a memcpy rather than a simultaneous call to all members'
assign-copy operators.
Obviously, a memcpy is not safe for all member objects, especially non-POD
objects, but that shouldn't mean that non-POD objects are prohibited from
being members of unions (especially anonymous ones). This is my issue.
1b) That unions with non-POD members may often require explicit
construction, copying, and destruction should not mean that they are simply
prohibited from being union members. Rather, this typical requirement should
be brought to the attention of programmers who make them members.
Any non-POD object can be a member of a union PROVIDED it is correctly
constructed, copied and destroyed (rather than use the default memcpy, and
no default construction/destruction).
So, please permit anonymous unions to contain non-POD members - given that
the user of an anonymous union will be clearly obliged to handle its
members' construction, assignment-copying, and destruction.
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: crosbie@quidmusic.com ("Crosbie Fitch")
Date: Thu, 1 Jun 2006 05:06:21 GMT Raw View
Anonymous union rules against members having constructors & non-default
assignment seem fairly arbitrary and unnecessary. :-)
template <>
class X
{
union
{ object d;
element<X,1> d1; // First pseudo member of d
element<X,2> d2; // Second pseudo member of d
};
};
..
X x,y;
x.d1=23; // Works fine element<> has operator=(int)
x.d2=123; // Fine
y.d1=5; // Fine too
y.d2=x.d1; // Fine - converts d2 to int
x.d2=y.d2; // Invokes default assignment operator :-(
The C std in its infinite wisdom says that members of an anonymous union can
do what the hell they want except they must not provide a default
constructor or assignment (or require a non-trivial assignment).
I neither want x.d2=y.d2 to do nothing, nor to do default assignment. What
can I do?
The only solution I've come up with is not really good enough, i.e.
template <>
class X
{
union
{ object d;
const element<X,1> d1;
const element<X,2> d2;
};
};
By adding const, and constifying the element::operator=(int) with a
const_cast to undo it, I can oblige a conversion rather than a default
assignment.
I can suffer the lack of constructors - that's fine. But, prohibiting
non-default assignment, WHY???
---
[ 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.comeaucomputing.com/csc/faq.html ]