aligned_storage in unions

Hi, While C++0x promises to change things (see draft spec section 9.5), C++ currently does not allow unions to contain members having a nontrivial constructor, operator=, or destructor. This excludes std::string, std::complex, structures containing either, etc. The "standard" workaround is to allocate your own memory and use manual type casting. Boost::aligned_storage can be used to maintain proper alignment, but it cannot be placed inside a union due to the default ctor, dtor, and noncopyable functions. Thus I am forced to use part of boost::detail. Here is a simple example. #include <boost/type_traits/aligned_storage.hpp> // define a union-safe wrapper template <class T> struct UnWrap { boost::detail::aligned_storage::aligned_storage_imp< sizeof(T), boost::alignment_of<T>::value> space; T * get() const { return static_cast<T *>(space.address()); } T * operator->() const { return get(); } T & operator*() const { return *get(); } }; union U { UnWrap<std::string> s; int x; }; U u; new (u.s.get()) std::string; *u.s="hi"; Questions: - Is there a better way to do this? - Am I doing something blatantly wrong? - Could aligned_storage_impl be exposed for such uses? (AIUI, the detail namespace contains unsupported internals.) Thanks, Daniel

AMDG dherring@ll.mit.edu wrote:
While C++0x promises to change things (see draft spec section 9.5), C++ currently does not allow unions to contain members having a nontrivial constructor, operator=, or destructor. This excludes std::string, std::complex, structures containing either, etc.
The "standard" workaround is to allocate your own memory and use manual type casting. Boost::aligned_storage can be used to maintain proper alignment, but it cannot be placed inside a union due to the default ctor, dtor, and noncopyable functions. Thus I am forced to use part of boost::detail.
Use boost::aligned_storage<...>::type which should be POD. In Christ, Steven Watanabe

On Fri, 17 Sep 2010, Steven Watanabe wrote:
dherring@ll.mit.edu wrote:
While C++0x promises to change things (see draft spec section 9.5), C++ currently does not allow unions to contain members having a nontrivial constructor, operator=, or destructor. This excludes std::string, std::complex, structures containing either, etc.
The "standard" workaround is to allocate your own memory and use manual type casting. Boost::aligned_storage can be used to maintain proper alignment, but it cannot be placed inside a union due to the default ctor, dtor, and noncopyable functions. Thus I am forced to use part of boost::detail.
Use boost::aligned_storage<...>::type which should be POD.
Thanks. Missed that typedef to the detail. Any other comments on the approach would be appreciated. - Daniel

On 09/17/2010 08:37 AM, dherring@ll.mit.edu wrote:
On Fri, 17 Sep 2010, Steven Watanabe wrote:
dherring@ll.mit.edu wrote:
While C++0x promises to change things (see draft spec section 9.5), C++ currently does not allow unions to contain members having a nontrivial constructor, operator=, or destructor. This excludes std::string, std::complex, structures containing either, etc.
The "standard" workaround is to allocate your own memory and use manual type casting. Boost::aligned_storage can be used to maintain proper alignment, but it cannot be placed inside a union due to the default ctor, dtor, and noncopyable functions. Thus I am forced to use part of boost::detail.
Use boost::aligned_storage<...>::type which should be POD.
Thanks. Missed that typedef to the detail. Any other comments on the approach would be appreciated.
- Daniel
You can also use boost::type_with_alignment<N>::type within a union to force a particular alignment. - Jeff

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On Friday 17 September 2010, dherring@ll.mit.edu wrote:
The "standard" workaround is to allocate your own memory and use manual type casting. Boost::aligned_storage can be used to maintain proper alignment, but it cannot be placed inside a union due to the default ctor, dtor, and noncopyable functions. Thus I am forced to use part of boost::detail.
Questions: - Is there a better way to do this?
What about using boost.variant instead? -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.9 (GNU/Linux) iEYEARECAAYFAkyTigwACgkQ5vihyNWuA4V9CwCfUeklRZYdk+v+iMR0is8KnTAW gwwAn1v7A9Qn5Fot58cKOHhwUxmAvOIR =jr/m -----END PGP SIGNATURE-----

On Fri, 17 Sep 2010, Frank Mori Hess wrote:
On Friday 17 September 2010, dherring@ll.mit.edu wrote:
The "standard" workaround is to allocate your own memory and use manual type casting. Boost::aligned_storage can be used to maintain proper alignment, but it cannot be placed inside a union due to the default ctor, dtor, and noncopyable functions. Thus I am forced to use part of boost::detail.
Questions: - Is there a better way to do this?
What about using boost.variant instead?
It doesn't satisfy my need for everything to be in-place (e.g. for seamless use with shared memory, memory pools, etc.). [Note: we don't actually use std::string; it was just convenient for the example.] Thanks, Daniel

On Fri, Sep 17, 2010 at 9:46 AM, <dherring@ll.mit.edu> wrote:
On Fri, 17 Sep 2010, Frank Mori Hess wrote:
On Friday 17 September 2010, dherring@ll.mit.edu wrote:
The "standard" workaround is to allocate your own memory and use manual type casting. Boost::aligned_storage can be used to maintain proper alignment, but it cannot be placed inside a union due to the default ctor, dtor, and noncopyable functions. Thus I am forced to use part of boost::detail.
Questions: - Is there a better way to do this?
What about using boost.variant instead?
It doesn't satisfy my need for everything to be in-place (e.g. for seamless use with shared memory, memory pools, etc.). [Note: we don't actually use std::string; it was just convenient for the example.]
How is it not in-place? If C++ supported unions containing anything, then boost::variant<int,std::string,myClass> would be identical in layout to: struct { uint which; union { int i; std::string s; myClass m; } } Which all use the same memory.

AMDG OvermindDL1 wrote:
On Fri, Sep 17, 2010 at 9:46 AM, <dherring@ll.mit.edu> wrote:
On Fri, 17 Sep 2010, Frank Mori Hess wrote:
What about using boost.variant instead?
It doesn't satisfy my need for everything to be in-place (e.g. for seamless use with shared memory, memory pools, etc.). [Note: we don't actually use std::string; it was just convenient for the example.]
How is it not in-place?
If C++ supported unions containing anything, then boost::variant<int,std::string,myClass> would be identical in layout to: struct { uint which; union { int i; std::string s; myClass m; } } Which all use the same memory.
boost::variant can use the heap in some cases, to preserve exception safety. In Christ, Steven Watanabe

On Fri, Sep 17, 2010 at 1:12 PM, Steven Watanabe <watanabesj@gmail.com> wrote:
AMDG
OvermindDL1 wrote:
On Fri, Sep 17, 2010 at 9:46 AM, <dherring@ll.mit.edu> wrote:
On Fri, 17 Sep 2010, Frank Mori Hess wrote:
What about using boost.variant instead?
It doesn't satisfy my need for everything to be in-place (e.g. for seamless use with shared memory, memory pools, etc.). [Note: we don't actually use std::string; it was just convenient for the example.]
How is it not in-place?
If C++ supported unions containing anything, then boost::variant<int,std::string,myClass> would be identical in layout to: struct { uint which; union { int i; std::string s; myClass m; } } Which all use the same memory.
boost::variant can use the heap in some cases, to preserve exception safety.
And when using recursive structures that do not manage their own recursiveness.

On 09/17/10 13:31, OvermindDL1 wrote:
On Fri, Sep 17, 2010 at 9:46 AM, <dherring@ll.mit.edu> wrote: [snip]
It doesn't satisfy my need for everything to be in-place (e.g. for seamless use with shared memory, memory pools, etc.). [Note: we don't actually use std::string; it was just convenient for the example.]
How is it not in-place?
If C++ supported unions containing anything, then boost::variant<int,std::string,myClass> would be identical in layout to: struct { uint which; union { int i; std::string s; myClass m; } } Which all use the same memory.
Maybe Daniel doesn't want to pay for the extra memory required by the which. At least that's the only reason I can think of :( So, Daniel, I've the same question as OvermindDl1. Could you clarify "in-place"? -regards, Larry

On Sat, 18 Sep 2010, Larry Evans wrote:
On 09/17/10 13:31, OvermindDL1 wrote:
On Fri, Sep 17, 2010 at 9:46 AM, <dherring@ll.mit.edu> wrote: [snip]
It doesn't satisfy my need for everything to be in-place (e.g. for seamless use with shared memory, memory pools, etc.). [Note: we don't actually use std::string; it was just convenient for the example.]
How is it not in-place? ... Maybe Daniel doesn't want to pay for the extra memory required by the which. At least that's the only reason I can think of :(
See http://www.boost.org/doc/libs/1_44_0/doc/html/variant/design.html In particular, "Temporary Heap Backup" and "Future Direction". We have some specialized requirements. We couldn't even use boost::shared_ptr until it added both the custom allocator and deleter. The "never-empty guarantee" conflicts with our needs and is not a concern at this time. As a side note, we also must expose both the type of the "which" and the discriminant to member mapping (which may be N:1; see the CORBA IDL spec). So I believe boost::variant would still be hard to use even if the heap issue were resolved. Later, Daniel

AMDG On 9/20/2010 8:50 AM, dherring@ll.mit.edu wrote:
See http://www.boost.org/doc/libs/1_44_0/doc/html/variant/design.html
In particular, "Temporary Heap Backup" and "Future Direction".
We have some specialized requirements. We couldn't even use boost::shared_ptr until it added both the custom allocator and deleter. The "never-empty guarantee" conflicts with our needs and is not a concern at this time.
You can effectively bypass the never-empty guarantee by adding boost::blank to the variant types. This will cause the variant to set itself to boost::blank on an exception instead of using heap backup. (This will actually work if has_nothrow_constructor is true for /any/ of the variant types, but boost::blank is the preferred backup if it's present.).
As a side note, we also must expose both the type of the "which" and the discriminant to member mapping (which may be N:1; see the CORBA IDL spec). So I believe boost::variant would still be hard to use even if the heap issue were resolved.
In Christ, Steven Watanabe

On Mon, 20 Sep 2010, Steven Watanabe wrote:
On 9/20/2010 8:50 AM, dherring@ll.mit.edu wrote:
See http://www.boost.org/doc/libs/1_44_0/doc/html/variant/design.html
In particular, "Temporary Heap Backup" and "Future Direction".
We have some specialized requirements. We couldn't even use boost::shared_ptr until it added both the custom allocator and deleter. The "never-empty guarantee" conflicts with our needs and is not a concern at this time.
You can effectively bypass the never-empty guarantee by adding boost::blank to the variant types. This will cause the variant to set itself to boost::blank on an exception instead of using heap backup. (This will actually work if has_nothrow_constructor is true for /any/ of the variant types, but boost::blank is the preferred backup if it's present.).
Thanks for the info; I hadn't noticed that feature. - Daniel

On 09/20/10 10:50, dherring@ll.mit.edu wrote:
On Sat, 18 Sep 2010, Larry Evans wrote:
On 09/17/10 13:31, OvermindDL1 wrote:
On Fri, Sep 17, 2010 at 9:46 AM, <dherring@ll.mit.edu> wrote: [snip]
It doesn't satisfy my need for everything to be in-place (e.g. for seamless use with shared memory, memory pools, etc.). [Note: we don't actually use std::string; it was just convenient for the example.]
How is it not in-place? ... Maybe Daniel doesn't want to pay for the extra memory required by the which. At least that's the only reason I can think of :(
See http://www.boost.org/doc/libs/1_44_0/doc/html/variant/design.html
In particular, "Temporary Heap Backup" and "Future Direction".
We have some specialized requirements. We couldn't even use boost::shared_ptr until it added both the custom allocator and deleter. The "never-empty guarantee" conflicts with our needs and is not a concern at this time.
As a side note, we also must expose both the type of the "which" and the discriminant to member mapping (which may be N:1; see the CORBA IDL spec). So I believe boost::variant would still be hard to use even if the heap issue were resolved.
Later, Daniel
An alternative to boost::variant is the: http://svn.boost.org/svn/boost/sandbox/variadic_templates /boost/composite_storage/pack/container_one_of_maybe.hpp which *might* work for you. I say *might* because I don't know exactly what you mean by: "never-empty guarantee" conflicts with our needs because those needs are not spelled out. The container_one_of_maybe doesn't have a "never empty" guarantee. Instead, it allows the container to have an uninitialized state indicated by which() returning a special value. Also, the type of which() can be user specified which may satisfy your requirement to: expose the type of the "which" This user specification is done by specializing: template < typename Index //2nd arg to composite_storage template //e.g. mpl::integral_c<int,Index0> //where Index0 is index of 1s element //of the components. > struct enum_base { typedef //type of discriminant. For example, int or char. //Usually some function of Index. type ; }; I'm also unsure of what's meant by: the discriminant to member mapping (which may be N:1). If you mean types could be duplicated, IOW, allow: variant<int,int> then that's what one_of_maybe does. The one place I found for corba spec (found with google "corba idl spec") was here: http://www.objs.com/x3h7/corbaidl.htm A search for "discrimin" on that page found: discriminated union type consistingof a discriminator followed by an instance of a type appropriate to thediscriminator value This layout (discriminant followed by dependent value) is the exact reverse of the container_one_of_maybe is laid out. Originally, the discrimiant was 1st; however, the following post: http://article.gmane.org/gmane.comp.parsers.spirit.general/19226 explains the reason for the change. It also shows the location of the non-variadic template version of the code. If you really need the order reversed, it could be changed back without much trouble. I guess it could be made a policy for one_of_maybe; however, that would mean another template parameter :( Hope that was clear enough ;) Please let me know if not. -regards, Larry

On Mon, 20 Sep 2010, Larry Evans wrote:
On 09/20/10 10:50, dherring@ll.mit.edu wrote:
See http://www.boost.org/doc/libs/1_44_0/doc/html/variant/design.html
In particular, "Temporary Heap Backup" and "Future Direction".
We have some specialized requirements. We couldn't even use boost::shared_ptr until it added both the custom allocator and deleter. The "never-empty guarantee" conflicts with our needs and is not a concern at this time.
An alternative to boost::variant is the:
http://svn.boost.org/svn/boost/sandbox/variadic_templates /boost/composite_storage/pack/container_one_of_maybe.hpp
which *might* work for you. I say *might* because I don't know exactly what you mean by:
"never-empty guarantee" conflicts with our needs
because those needs are not spelled out.
Rephrase: We don't need the never-empty guarantee; but it conflicts with the always-in-place requirement. Steven's suggestion to add boost::blank looks promising. The documentation should be updated to mention it. This container looks interesting; but other than the sources, I don't see any documentation? For now, we'll stick with what I've cobbled together.
As a side note, we also must expose both the type of the "which" and the discriminant to member mapping (which may be N:1; see the CORBA IDL spec). So I believe boost::variant would still be hard to use even if the heap issue were resolved.
I'm also unsure of what's meant by:
the discriminant to member mapping (which may be N:1).
If you mean types could be duplicated, IOW, allow:
variant<int,int>
then that's what one_of_maybe does.
The one place I found for corba spec (found with google "corba idl spec") was here:
If you're interested, see also Part 1, section 7.11.2.2 of http://www.omg.org/spec/CORBA/3.1/ and the C++ mapping on http://www.omg.org/technology/documents/idl2x_spec_catalog.htm We support a slight variant of this spec.
This layout (discriminant followed by dependent value) is the exact reverse of the container_one_of_maybe is laid out. Originally, the discrimiant was 1st; however, the following post:
That order doesn't bother me too much. The "N:1" notation meant that, just like a "switch statement", there may be N cases that specify one member. It is also possible for multiple members to have the same type. For example, here's an IDL snippet. It shows the discriminant is a "long"; both default and 2 specify member y. union U switch(long) { case 0: long x; default: case 2: char y[10]; case 3: float z; }; Later, Daniel

On 09/20/10 13:56, dherring@ll.mit.edu wrote:
On Mon, 20 Sep 2010, Larry Evans wrote:
On 09/20/10 10:50, dherring@ll.mit.edu wrote:
See http://www.boost.org/doc/libs/1_44_0/doc/html/variant/design.html
In particular, "Temporary Heap Backup" and "Future Direction".
We have some specialized requirements. We couldn't even use boost::shared_ptr until it added both the custom allocator and deleter. The "never-empty guarantee" conflicts with our needs and is not a concern at this time.
An alternative to boost::variant is the:
http://svn.boost.org/svn/boost/sandbox/variadic_templates /boost/composite_storage/pack/container_one_of_maybe.hpp
which *might* work for you. I say *might* because I don't know exactly what you mean by:
"never-empty guarantee" conflicts with our needs
because those needs are not spelled out.
Rephrase: We don't need the never-empty guarantee; but it conflicts with the always-in-place requirement. Steven's suggestion to add boost::blank looks promising. The documentation should be updated to mention it.
I'm still unsure what "always-in-place" means. Based on what Steven was saying, I'm guessing it means only stack or static storage can be used (i.e. no calls to new or malloc). Is that right? If so, then one_of_maybe satisfies that requirement. However, unlike boost::variant, it makes no provision for failure of operator= when the source and target are different types; however, that could be remedied by simply catching any throw and setting which() to indicate the one_of_maybe was uninitialized. Since the uninitialized value ( of type nothing, defined here: http://svn.boost.org/svn/boost/sandbox/variadic_templates/boost /composite_storage/special_components.hpp ) cannot throw during construction, it would workaround the operator= problem.
This container looks interesting; but other than the sources, I don't see any documentation?
Will work on that ;)
For now, we'll stick with what I've cobbled together.
As a side note, we also must expose both the type of the "which" and the discriminant to member mapping (which may be N:1; see the CORBA IDL spec). So I believe boost::variant would still be hard to use even if the heap issue were resolved.
I'm also unsure of what's meant by:
the discriminant to member mapping (which may be N:1).
If you mean types could be duplicated, IOW, allow:
variant<int,int>
then that's what one_of_maybe does.
The one place I found for corba spec (found with google "corba idl spec") was here:
If you're interested, see also Part 1, section 7.11.2.2 of http://www.omg.org/spec/CORBA/3.1/ and the C++ mapping on http://www.omg.org/technology/documents/idl2x_spec_catalog.htm
We support a slight variant of this spec.
This layout (discriminant followed by dependent value) is the exact reverse of the container_one_of_maybe is laid out. Originally, the discrimiant was 1st; however, the following post:
That order doesn't bother me too much. The "N:1" notation meant that, just like a "switch statement", there may be N cases that specify one member. It is also possible for multiple members to have the same type.
[snip] This is what I meant to imply by the example: variant<int,int> With the current variant, this won't work; however, it's perfectly OK with one_of_maybe. One reason I wanted this was to emulate the way lex works, which returns a discriminated union. Most of the types in the union are simply unused because one's not really interested in the character string representation of some keyword, instead, one is only interested in which keyword was found, and that's encoded in the discriminant. One_of_maybe has another special type for this very purpose. It takes up no room (there's a specialization of size_of template which returns 0 for this special type); hence, it doesn't contribute to the calculation of the size of one_of_maybe.
Later, Daniel
Regards, Larry

On 09/17/10 10:11, dherring@ll.mit.edu wrote:
Hi,
While C++0x promises to change things (see draft spec section 9.5), C++ currently does not allow unions to contain members having a nontrivial constructor, operator=, or destructor. This excludes std::string, std::complex, structures containing either, etc.
The "standard" workaround is to allocate your own memory and use manual type casting. Boost::aligned_storage can be used to maintain proper alignment, but it cannot be placed inside a union due to the default ctor, dtor, and noncopyable functions. Thus I am forced to use part of boost::detail.
Here is a simple example.
#include <boost/type_traits/aligned_storage.hpp>
// define a union-safe wrapper template <class T> struct UnWrap { boost::detail::aligned_storage::aligned_storage_imp< sizeof(T), boost::alignment_of<T>::value> space;
T * get() const { return static_cast<T *>(space.address()); } T * operator->() const { return get(); } T & operator*() const { return *get(); } };
union U { UnWrap<std::string> s; int x; };
U u; new (u.s.get()) std::string; *u.s="hi";
Questions: - Is there a better way to do this? - Am I doing something blatantly wrong? - Could aligned_storage_impl be exposed for such uses? (AIUI, the detail namespace contains unsupported internals.)
Thanks, Daniel
Hi Daniel, Why not use boost::variant<std::string,int>? Using your example: boost::variant<std::string,int> u; u = "hi"; assert(u.which()==1); u = 9; assert(u.which()==2); (I may have the which results off by one, but you get the idea). -regards, Larry

On 17/09/2010 16:11, dherring@ll.mit.edu wrote:
The "standard" workaround is to allocate your own memory and use manual type casting. Boost::aligned_storage can be used to maintain proper alignment, but it cannot be placed inside a union due to the default ctor, dtor, and noncopyable functions. Thus I am forced to use part of boost::detail.
That approach is fundamentally broken as this is not allowed by the strict aliasing rules of C++. Every other utility that uses it, such as variant and optional, are broken too; but they've got no other choice for their implementation, unfortunately, at least until C++0x.

On 09/22/10 05:20, Mathias Gaunard wrote: [snip]
That approach is fundamentally broken as this is not allowed by the strict aliasing rules of C++.
Every other utility that uses it, such as variant and optional, are broken too; but they've got no other choice for their implementation, unfortunately, at least until C++0x.
How would C++0x avoid the problem? A google of "strict aliasing rule" turned up: http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-al... which says: Strict aliasing is an assumption, made by the C (or C++) compiler, that dereferencing pointers to objects of different types will never refer to the same memory location (i.e. alias each other.) Could you point to the variant code violating this rule? TIA. -Larry

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On Wednesday 22 September 2010, Larry Evans wrote:
Strict aliasing is an assumption, made by the C (or C++) compiler, that dereferencing pointers to objects of different types will never refer to the same memory location (i.e. alias each other.)
Could you point to the variant code violating this rule?
How about if you re-assign a variant to a different type. Won't it re-use the same in-place storage for a different type? -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.9 (GNU/Linux) iEYEARECAAYFAkyaRDQACgkQ5vihyNWuA4VHuQCfeQbarKIF3O0+gTkyXlj6V5eT Ev8AoJTL7D1oeV3E3uejqnQdSMgCY4Ak =hugK -----END PGP SIGNATURE-----

On 09/22/10 13:00, Frank Mori Hess wrote:
On Wednesday 22 September 2010, Larry Evans wrote:
Strict aliasing is an assumption, made by the C (or C++) compiler, that dereferencing pointers to objects of different types will never refer to the same memory location (i.e. alias each other.)
Could you point to the variant code violating this rule?
How about if you re-assign a variant to a different type. Won't it re-use the same in-place storage for a different type?
Yes, AFAICT. Thanks. So now my other question: how can C++0x avoid this?

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On Wednesday 22 September 2010, Larry Evans wrote:
On 09/22/10 13:00, Frank Mori Hess wrote:
On Wednesday 22 September 2010, Larry Evans wrote:
Strict aliasing is an assumption, made by the C (or C++) compiler, that dereferencing pointers to objects of different types will never refer to the same memory location (i.e. alias each other.)
Could you point to the variant code violating this rule?
How about if you re-assign a variant to a different type. Won't it re-use the same in-place storage for a different type?
Yes, AFAICT. Thanks.
So now my other question: how can C++0x avoid this?
I think it would be because C++0x allows non-pod types in unions. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.9 (GNU/Linux) iEYEARECAAYFAkyaTRgACgkQ5vihyNWuA4WAbQCeJ8LhMsaj4OkEJT2Jm4tM96to XgMAoJ8TkOfY4gct23j6UrXeYIchtahr =O9Qv -----END PGP SIGNATURE-----

On 09/22/10 13:00, Frank Mori Hess wrote: [snip]
How about if you re-assign a variant to a different type. Won't it re-use the same in-place storage for a different type?
Yes, AFAICT. Thanks. Rethought that answer because I don't (yet) see where pointers to the 2 different types exist at the same time in the operator= for
On 09/22/10 13:14, Larry Evans wrote: the variant implementation here: https://svn.boost.org/trac/boost/browser/sandbox/variadic_templates/boost/co... The operator= here: https://svn.boost.org/trac/boost/browser/sandbox/variadic_templates/boost/co... first calls the DTOR for the lhs by calling destroy() and only within destroy is the pointer to the lhs used. After that, the value of the rhs is constructed in the memory of the lhs via the assign_copy(from). A cursory look at the assign_copy implementation doesn't show anything used except the buffer for the lhs, which makes sense becasue all that's required is just a call to the placement new for using the buffer for the lhs and the value from the rhs. I would imagine boost::variant would do something similar, but maybe Mathias knows differently. Mathias? -Larry

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On Wednesday 22 September 2010, Larry Evans wrote:
On 09/22/10 13:14, Larry Evans wrote:
On 09/22/10 13:00, Frank Mori Hess wrote:
[snip]
How about if you re-assign a variant to a different type. Won't it re-use the same in-place storage for a different type?
Yes, AFAICT. Thanks.
Rethought that answer because I don't (yet) see where pointers to the 2 different types exist at the same time in the operator= for
I don't see how that gets around the anti-aliasing rules. The compiler is allowed to assume incompatible pointers won't _ever_ alias. So just because your program does things in a certain order doesn't mean the compiler isn't allowed to use the anti-aliasing assumptions to reorder things in a nasty way. Couldn't it look at the destruction of the old object and the construction of the new object as two completely independent actions it is free to reorder or interleave? -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.9 (GNU/Linux) iEYEARECAAYFAkyaU0kACgkQ5vihyNWuA4VwNwCeLT5m2y27SxSucxI0Oa3nub/a 7zkAoMlKG5uvCJn3vAIAHRX1Xhg9wk7R =KExQ -----END PGP SIGNATURE-----

On 09/22/10 14:04, Frank Mori Hess wrote:
On Wednesday 22 September 2010, Larry Evans wrote: [snip]
Rethought that answer because I don't (yet) see where pointers to the 2 different types exist at the same time in the operator= for
I don't see how that gets around the anti-aliasing rules. The compiler is allowed to assume incompatible pointers won't _ever_ alias. So just because your program does things in a certain order doesn't mean the compiler isn't allowed to use the anti-aliasing assumptions to reorder things in a nasty way. Couldn't it look at the destruction of the old object and the construction of the new object as two completely independent actions it is free to reorder or interleave?
I hadn't thought of the compiler reordering the calls. Just to be clear, the compiler, using the anti-aliasing rules, could reorder: 316 destroy(); 317 assign_copy(from); to: 317 assign_copy(from); 316 destroy(); ? Or maybe just do everything in destroy before the lhs->~Lhs() call, then execute assign_copy(from) and then do lhs->~Lhs(). Hmm... OK, I think I got it, but am surprised. So now, using the C++0x non-pod in union capability; however, would one generate the attribute for Spirit's alternative parser (a | b): http://www.boost.org/doc/libs/1_44_0/libs/spirit/doc/html/spirit/qi/quick_re... ? Maybe one could do what fusion does for vector, i.e. using boost/preprocessory, generates: template<T1,T2,...,Tn> struct vector_n { T1 m1; T2 m2; ... Tn mn; }; Only for the union, it would be: template<T1,T2,...,Tn> struct union_n { union { T1 m1; T2 m2; ... Tn mn; } u; }; Or something like that? Otherwise, I don't see a workaround to this strict aliasing problem :( -Larry

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On Wednesday 22 September 2010, Larry Evans wrote:
On 09/22/10 14:04, Frank Mori Hess wrote:
I don't see how that gets around the anti-aliasing rules. The compiler is allowed to assume incompatible pointers won't _ever_ alias. So just because your program does things in a certain order doesn't mean the compiler isn't allowed to use the anti-aliasing assumptions to reorder things in a nasty way. Couldn't it look at the destruction of the old object and the construction of the new object as two completely independent actions it is free to reorder or interleave?
I hadn't thought of the compiler reordering the calls. Just to be clear, the compiler, using the anti-aliasing rules, could reorder:
316 destroy(); 317 assign_copy(from);
to:
317 assign_copy(from); 316 destroy();
? Or maybe just do everything in destroy before the lhs->~Lhs() call, then execute assign_copy(from) and then do lhs->~Lhs(). Hmm... OK, I think I got it, but am surprised.
I think so, in principle. I only have a vague idea of what optimizing compilers actually do under the hood though, which is why I tend to be very conservative about obeying the strict aliasing rules (they've bitten me before). -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.9 (GNU/Linux) iEYEARECAAYFAkyaZvIACgkQ5vihyNWuA4W0ZACg2fcasot2btTi72ddKDI8BaZI svoAnRpUsfH2XPOs5bAFj9DaEYsJEGPU =RdVG -----END PGP SIGNATURE-----

On 09/22/10 15:28, Frank Mori Hess wrote:
On Wednesday 22 September 2010, Larry Evans wrote:
On 09/22/10 14:04, Frank Mori Hess wrote:
I don't see how that gets around the anti-aliasing rules. The compiler is allowed to assume incompatible pointers won't _ever_ alias. So just because your program does things in a certain order doesn't mean the compiler isn't allowed to use the anti-aliasing assumptions to reorder things in a nasty way. Couldn't it look at the destruction of the old object and the construction of the new object as two completely independent actions it is free to reorder or interleave?
I hadn't thought of the compiler reordering the calls. Just to be clear, the compiler, using the anti-aliasing rules, could reorder:
316 destroy(); 317 assign_copy(from);
to:
317 assign_copy(from); 316 destroy();
? Or maybe just do everything in destroy before the lhs->~Lhs() call, then execute assign_copy(from) and then do lhs->~Lhs(). Hmm... OK, I think I got it, but am surprised.
I think so, in principle. I only have a vague idea of what optimizing compilers actually do under the hood though, which is why I tend to be very conservative about obeying the strict aliasing rules (they've bitten me before).
A more concrete example is attached. IOW, the strict aliasing rules would allow the compiler to move: p20->~str(); after the: str<10>* p10=new(buf) str<10>; because p20 and p10 are assumed not pointing to the same location. Note: I did compile with: -O3 -Wstrict-aliasing as Mathias suggested; however, the compiler didn't issue any warnings. I'm wondering why? -Larry

----- Original Message ----- From: "Larry Evans" <cppljevans@suddenlink.net> To: <boost@lists.boost.org> Sent: Wednesday, September 22, 2010 11:38 PM Subject: Re: [boost] aligned_storage in unions
On 09/22/10 15:28, Frank Mori Hess wrote:
On Wednesday 22 September 2010, Larry Evans wrote:
On 09/22/10 14:04, Frank Mori Hess wrote:
I don't see how that gets around the anti-aliasing rules. The compiler is allowed to assume incompatible pointers won't _ever_ alias. So just because your program does things in a certain order doesn't mean the compiler isn't allowed to use the anti-aliasing assumptions to reorder things in a nasty way. Couldn't it look at the destruction of the old object and the construction of the new object as two completely independent actions it is free to reorder or interleave?
I hadn't thought of the compiler reordering the calls. Just to be clear, the compiler, using the anti-aliasing rules, could reorder:
316 destroy(); 317 assign_copy(from);
to:
317 assign_copy(from); 316 destroy();
? Or maybe just do everything in destroy before the lhs->~Lhs() call, then execute assign_copy(from) and then do lhs->~Lhs(). Hmm... OK, I think I got it, but am surprised.
I think so, in principle. I only have a vague idea of what optimizing compilers actually do under the hood though, which is why I tend to be very conservative about obeying the strict aliasing rules (they've bitten me before).
A more concrete example is attached.
IOW, the strict aliasing rules would allow the compiler to move:
p20->~str();
after the:
str<10>* p10=new(buf) str<10>;
because p20 and p10 are assumed not pointing to the same location.
Note: I did compile with:
-O3 -Wstrict-aliasing
as Mathias suggested; however, the compiler didn't issue any warnings. I'm wondering why?
Hi, I don't know which version are you using, but the options differ from aversion to another. 4.1.2 -Wstrict-aliasing This option is only active when -fstrict-aliasing is active. It warns about code which might break the strict aliasing rules that the compiler is using for optimization. The warning does not catch all cases, but does attempt to catch the more common pitfalls. It is included in -Wall. -Wstrict-aliasing=2 This option is only active when -fstrict-aliasing is active. It warns about code which might break the strict aliasing rules that the compiler is using for optimization. This warning catches more cases than -Wstrict-aliasing, but it will also give a warning for some ambiguous cases that are safe. 4.5 -Wstrict-aliasing This option is only active when -fstrict-aliasing is active. It warns about code which might break the strict aliasing rules that the compiler is using for optimization. The warning does not catch all cases, but does attempt to catch the more common pitfalls. It is included in -Wall. It is equivalent to -Wstrict-aliasing=3 -Wstrict-aliasing=n This option is only active when -fstrict-aliasing is active. It warns about code which might break the strict aliasing rules that the compiler is using for optimization. Higher levels correspond to higher accuracy (fewer false positives). Higher levels also correspond to more effort, similar to the way -O works. -Wstrict-aliasing is equivalent to -Wstrict-aliasing=n, with n=3. Level 1: Most aggressive, quick, least accurate. Possibly useful when higher levels do not warn but -fstrict-aliasing still breaks the code, as it has very few false negatives. However, it has many false positives. Warns for all pointer conversions between possibly incompatible types, even if never dereferenced. Runs in the frontend only. Level 2: Aggressive, quick, not too precise. May still have many false positives (not as many as level 1 though), and few false negatives (but possibly more than level 1). Unlike level 1, it only warns when an address is taken. Warns about incomplete types. Runs in the frontend only. Level 3 (default for -Wstrict-aliasing): Should have very few false positives and few false negatives. Slightly slower than levels 1 or 2 when optimization is enabled. Takes care of the common pun+dereference pattern in the frontend: *(int*)&some_float. If optimization is enabled, it also runs in the backend, where it deals with multiple statement cases using flow-sensitive points-to information. Only warns when the converted pointer is dereferenced. Does not warn about incomplete types. HTH, Vicente

On 09/22/10 17:02, vicente.botet wrote:
----- Original Message ----- From: "Larry Evans" <cppljevans@suddenlink.net> [snip]
Note: I did compile with:
-O3 -Wstrict-aliasing
as Mathias suggested; however, the compiler didn't issue any warnings. I'm wondering why?
Hi,
I don't know which version are you using, but the options differ from aversion to another.
4.1.2 -Wstrict-aliasing This option is only active when -fstrict-aliasing is active. It warns about code which might break the strict aliasing rules that the compiler is using for optimization. The warning does not catch all cases, but does attempt to catch the more common pitfalls. It is included in -Wall.
-Wstrict-aliasing=2 This option is only active when -fstrict-aliasing is active. It warns about code which might break the strict aliasing rules that the compiler is using for optimization. This warning catches more cases than -Wstrict-aliasing, but it will also give a warning for some ambiguous cases that are safe.
4.5 -Wstrict-aliasing This option is only active when -fstrict-aliasing is active. It warns about code which might break the strict aliasing rules that the compiler is using for optimization. The warning does not catch all cases, but does attempt to catch the more common pitfalls. It is included in -Wall. It is equivalent to -Wstrict-aliasing=3
-Wstrict-aliasing=n This option is only active when -fstrict-aliasing is active. It warns about code which might break the strict aliasing rules that the compiler is using for optimization. Higher levels correspond to higher accuracy (fewer false positives). Higher levels also correspond to more effort, similar to the way -O works. -Wstrict-aliasing is equivalent to -Wstrict-aliasing=n, with n=3. Level 1: Most aggressive, quick, least accurate. Possibly useful when higher levels do not warn but -fstrict-aliasing still breaks the code, as it has very few false negatives. However, it has many false positives. Warns for all pointer conversions between possibly incompatible types, even if never dereferenced. Runs in the frontend only.
Level 2: Aggressive, quick, not too precise. May still have many false positives (not as many as level 1 though), and few false negatives (but possibly more than level 1). Unlike level 1, it only warns when an address is taken. Warns about incomplete types. Runs in the frontend only.
Level 3 (default for -Wstrict-aliasing): Should have very few false positives and few false negatives. Slightly slower than levels 1 or 2 when optimization is enabled. Takes care of the common pun+dereference pattern in the frontend: *(int*)&some_float. If optimization is enabled, it also runs in the backend, where it deals with multiple statement cases using flow-sensitive points-to information. Only warns when the converted pointer is dereferenced. Does not warn about incomplete types.
HTH,
Thanks Vicente. I'm using gcc version 4.5. I changed to to actually dereference the pointer. I also added the -fstrict-aliasing flag; however, I'm still not getting any errors. Code and compilation output attached. -Larry

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On Wednesday 22 September 2010, Larry Evans wrote:
I'm using gcc version 4.5.
I changed to to actually dereference the pointer. I also added the -fstrict-aliasing flag; however, I'm still not getting any errors.
Code and compilation output attached.
-Larry
You know, after re-reading the standard, I think the placement new makes it okay (aside from the undefined behaviour in your example of accessing a destroyed object). The standard only refers to objects and their dynamic types, as opposed to memory addresses (which is what I had in my head). So the placement new would set the dynamic type of the new object, and then it is okay to access it through an lvalue expression that is compatible with the new dynamic type of the object. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.9 (GNU/Linux) iEYEARECAAYFAkybVbUACgkQ5vihyNWuA4VtrQCgyJWKLw+lGRrGJXTODkmpZ43n waoAnjcd/FhKeESobxmiFIeFMVC/ncKP =rI7b -----END PGP SIGNATURE-----

On 22/09/2010 18:35, Larry Evans wrote:
How would C++0x avoid the problem?
By allowing to put non-PODs in unions.
Could you point to the variant code violating this rule?
aligned_storage<whatever>::type is a POD type of a certain type T. In order to read and write any of the object a variant can hold, it will need to reinterpret cast from a pointer to that object to a pointer to any of these object types, that are not necessarily the same as T.

On 09/22/10 13:46, Mathias Gaunard wrote:
On 22/09/2010 18:35, Larry Evans wrote:
How would C++0x avoid the problem?
By allowing to put non-PODs in unions.
Could you point to the variant code violating this rule?
aligned_storage<whatever>::type is a POD type of a certain type T. In order to read and write any of the object a variant can hold, it will need to reinterpret cast from a pointer to that object to a pointer to any of these object types, that are not necessarily the same as T.
I thought variant just static_cast<char*> to void* then from void* to T*. The char* is for the memory buffer in boost::aligned_storage. IOW, it doesn't use reinterpret_cast. To confirm, I did: find . -name \*.hpp -exec grep _cast {} \; -ls in boost/variant and only found static_cast. Now, as far as Frank's point about operator=, my reply to his post indicated there would never be a case where two pointers of different types and pointing to the same memory location would be in scope at the same time in the operator= implementation, at least for the container_one_of_maybe mentioned in my reply. I don't know if that is true for boost::variant. -Larry

On 22/09/2010 20:05, Larry Evans wrote:
I thought variant just static_cast<char*> to void* then from void* to T*.
As far as I'm concerned, this is the same thing.
The char* is for the memory buffer in boost::aligned_storage. IOW, it doesn't use reinterpret_cast. To confirm, I did:
find . -name \*.hpp -exec grep _cast {} \; -ls
in boost/variant and only found static_cast.
Now, as far as Frank's point about operator=, my reply to his post indicated there would never be a case where two pointers of different types and pointing to the same memory location would be in scope at the same time in the operator= implementation, at least for the container_one_of_maybe mentioned in my reply. I don't know if that is true for boost::variant.
A first try is to ask GCC if you break the strict aliasing rules. Compile some test code with -O3 -Wstrict-aliasing and see if it says something.

On 09/22/10 15:03, Mathias Gaunard wrote: [snip]
A first try is to ask GCC if you break the strict aliasing rules. Compile some test code with -O3 -Wstrict-aliasing and see if it says something.
It's doesn't complain, as shown in attachment. Of course I realize that just means gcc didn't detect a violation of the rule, not that there actually wasn't any violation ;) The code tested should be: https://svn.boost.org/trac/boost/browser/sandbox/variadic_templates/libs/com...

On 09/22/10 15:03, Mathias Gaunard wrote: [snip]
A first try is to ask GCC if you break the strict aliasing rules. Compile some test code with -O3 -Wstrict-aliasing and see if it says something.
When Compiling the attached, gcc doesn't complain. I thought surely it would since p10 and p20 both point to the same location but they're different types. What am I missing? -Larry

On Sep 22, 2010, at 4:03 PM, Mathias Gaunard wrote:
I thought variant just static_cast<char*> to void* then from void* to T*.
As far as I'm concerned, this is the same thing.
But the standard disagrees. -- Dave Abrahams BoostPro Computing http://boostpro.com

On Sep 22, 2010, at 2:46 PM, Mathias Gaunard wrote:
On 22/09/2010 18:35, Larry Evans wrote:
How would C++0x avoid the problem?
By allowing to put non-PODs in unions.
Could you point to the variant code violating this rule?
aligned_storage<whatever>::type is a POD type of a certain type T. In order to read and write any of the object a variant can hold, it will need to reinterpret cast from a pointer to that object to a pointer to any of these object types, that are not necessarily the same as T.
static_cast through void*, please. That has defined behavior, whereas reinterpret_cast is only implementation-defined. -- Dave Abrahams BoostPro Computing http://boostpro.com

On Sep 22, 2010, at 4:01 PM, Mathias Gaunard wrote:
On 22/09/2010 20:43, David Abrahams wrote:
static_cast through void*, please. That has defined behavior
AFAIK, not if you don't cast back to exactly the same type that was converted to void*.
Yes, in many cases, even if you don't. For example, if you want to access the raw bytes of an object, you can do static_cast<char*>(static_cast<void*>(&x)) Also, if you happen to know that X is a POD struct beginning with an int, you can *static_cast<int*>(static_cast<void*>(&x)) = 0; -- Dave Abrahams BoostPro Computing http://boostpro.com

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On Thursday 23 September 2010, David Abrahams wrote:
On Sep 22, 2010, at 4:01 PM, Mathias Gaunard wrote:
On 22/09/2010 20:43, David Abrahams wrote:
static_cast through void*, please. That has defined behavior
AFAIK, not if you don't cast back to exactly the same type that was converted to void*.
Yes, in many cases, even if you don't. For example, if you want to access the raw bytes of an object, you can do
static_cast<char*>(static_cast<void*>(&x))
Also, if you happen to know that X is a POD struct beginning with an int, you can
*static_cast<int*>(static_cast<void*>(&x)) = 0;
What is this based on? There is a strict aliasing exception for char*, but all I see in the standard about static_casting through void is (paragraph 10 of 5.2.9): "A value of type pointer to object converted to “pointer to cv void” and back to the original pointer type will have its original value." -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.9 (GNU/Linux) iEYEARECAAYFAkybdDgACgkQ5vihyNWuA4WVIACfSYvqL5w1Os5RyWQI0gJOH4Rf idQAoLyoBZHhwEbSegc65JY+Cx4EyPWo =UIov -----END PGP SIGNATURE-----

On Sep 23, 2010, at 11:37 AM, Frank Mori Hess wrote:
On Thursday 23 September 2010, David Abrahams wrote:
On Sep 22, 2010, at 4:01 PM, Mathias Gaunard wrote:
On 22/09/2010 20:43, David Abrahams wrote:
static_cast through void*, please. That has defined behavior
AFAIK, not if you don't cast back to exactly the same type that was converted to void*.
Yes, in many cases, even if you don't. For example, if you want to access the raw bytes of an object, you can do
static_cast<char*>(static_cast<void*>(&x))
Also, if you happen to know that X is a POD struct beginning with an int, you can
*static_cast<int*>(static_cast<void*>(&x)) = 0;
What is this based on? There is a strict aliasing exception for char*, but all I see in the standard about static_casting through void is (paragraph 10 of 5.2.9):
Sorry, no time to trawl the standard right now. The above matches my hard-won understanding but of course I could always be wrong about anything ;-) -- Dave Abrahams BoostPro Computing http://boostpro.com

From: David Abrahams <dave@boostpro.com>
To: "boost@lists.boost.org" <boost@lists.boost.org> Sent: Thu, September 23, 2010 7:18:22 PM Subject: Re: [boost] aligned_storage in unions
On Sep 23, 2010, at 11:37 AM, Frank Mori Hess wrote:
What is this based on? There is a strict aliasing exception for char*, but
all I see in the standard about static_casting through void is (paragraph 10
of 5.2.9):
Sorry, no time to trawl the standard right now. The above matches my hard-won understanding but of course I could always be wrong about anything ;-)
I think u mean 9.2/19: "A pointer to a standard-layout struct object, suitably converted using a reinterpret_cast, points to its initial member (or if that member is a bit-field, then to the unit in which it resides) and vice versa. [ Note: There might therefore be unnamed padding within a standard-layout struct object, but not at its beginning, as necessary to achieve appropriate alignment. =97end note ]"
-- Dave Abrahams BoostPro Computing http://boostpro.com
participants (10)
-
David Abrahams
-
dherring@ll.mit.edu
-
Frank Mori Hess
-
Jeffrey Lee Hellrung, Jr.
-
Larry Evans
-
Mathias Gaunard
-
OvermindDL1
-
Steven Watanabe
-
Trigve Siver
-
vicente.botet