
Hi! I have written a small utility class to provide "empty member optimization" for arbitrary types. The implementation makes use of the empty base class optimization (EBCO) but avoids injection of the class. A uniform and contained interface is exposed wether the optimization applies or not: template <typename T, size_t Index> compressed_pair { // several constructors may be provided but, for now, // default and copy constructor typedef T value_type; value_type & get(); value_type const & get() const; }; I believe this could be used as the basis for a compressed_tuple. Here's a sample use: template <typename T0, typename T1> struct A : private compressed_member<T0> , private compressed_member<T1, 1> { typedef compressed_member<T0> member0; typedef compressed_member<T1, 1> member1; T0 const & first() { return base0::get(); } T1 const & second() { return base1::get(); } }; There is a catch with the current implementation: some regular empty member (or regular empty member of some compressed_member) could end up sharing address with an empty compressed_member. This happens because compressed_member<T> does not inherit from T but hides this "relationship" from the compiler. Is there any interest in such an utility? To test compressed member I wrote another (toy) utility: empty_store. empty_store is an empty class with a static map that associates an instance's address to data. Checks are made everywhere to ensure that no two instances share the same address. The code can be found (zipped) here: *http://tinyurl.com/4e85l* Best regards, João Abecasis

I have written a small utility class to provide "empty member optimization" for arbitrary types. The implementation makes use of the empty base class optimization (EBCO) but avoids injection of the class. A uniform and contained interface is exposed wether the optimization applies or not:
I believe this could be used as the basis for a compressed_tuple.
Here's a sample use:
template <typename T0, typename T1> struct A : private compressed_member<T0> , private compressed_member<T1, 1> { typedef compressed_member<T0> member0; typedef compressed_member<T1, 1> member1;
T0 const & first() { return base0::get(); } T1 const & second() { return base1::get(); } };
How does this differ from boost::compressed_pair ? John.

Joao Abecasis wrote:
Hi!
I have written a small utility class to provide "empty member optimization" for arbitrary types. The implementation makes use of the empty base class optimization (EBCO) but avoids injection of the class. A uniform and contained interface is exposed wether the optimization applies or not:
template <typename T, size_t Index> compressed_pair
Ooops... I meant compressed_member, above. Regards, João Abecasis

John Maddock wrote:
How does this differ from boost::compressed_pair ?
Well, compressed_pair applies to a pair and compressed_member applies memberwise. In a situation where you have more than two members compressed_member may compact better. Consider this situation: struct A {}; struct B {}; struct C : compressed_member<A> , compressed_member<A, 1> , compressed_member<B, 1> , compressed_member<B, 1> { }; Here, each A gets its address but the Bs can share those same addresses. I think applying the optimization memberwise may scale better for arbitrary tuples. Regards, João Abecasis

Joao Abecasis <jpabecasis@zmail.pt> writes:
John Maddock wrote:
How does this differ from boost::compressed_pair ?
Well, compressed_pair applies to a pair and compressed_member applies memberwise. In a situation where you have more than two members compressed_member may compact better.
Consider this situation:
struct A {}; struct B {};
struct C : compressed_member<A> , compressed_member<A, 1> , compressed_member<B, 1> , compressed_member<B, 1> { };
Here, each A gets its address but the Bs can share those same addresses. I think applying the optimization memberwise may scale better for arbitrary tuples.
I think the question is whether it's ever desirable to break the current C++ invariant that no two objects of the same type will ever share an address **in generic code** -- that is, when you don't know anything about the assumptions that may be made by that type. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

Well, compressed_pair applies to a pair and compressed_member applies memberwise. In a situation where you have more than two members compressed_member may compact better.
Consider this situation:
struct A {}; struct B {};
struct C : compressed_member<A> , compressed_member<A, 1> , compressed_member<B, 1> , compressed_member<B, 1> { };
Here, each A gets its address but the Bs can share those same addresses. I think applying the optimization memberwise may scale better for arbitrary tuples.
Except that most compilers don't implement the empty-base-optimisation when there is multiple inheritance taking place, in fact for at least one compile (Borland's) this is a serious dis-optimisation, with 8-bytes per base class getting added! The compressed_pair way would be: struct C { private: boost::compressed_pair< empty_type1, boost::compressed_pair< empty_type2, non_empty_type> > m_data; }; and of course this way struct C doesn't have any "accidental" base classes, not even private ones. John.

"John Maddock" <john@johnmaddock.co.uk> writes:
Well, compressed_pair applies to a pair and compressed_member applies memberwise. In a situation where you have more than two members compressed_member may compact better.
Consider this situation:
struct A {}; struct B {};
struct C : compressed_member<A> , compressed_member<A, 1> , compressed_member<B, 1> , compressed_member<B, 1> { };
Here, each A gets its address but the Bs can share those same addresses. I think applying the optimization memberwise may scale better for arbitrary tuples.
Except that most compilers don't implement the empty-base-optimisation when there is multiple inheritance taking place, in fact for at least one compile (Borland's) this is a serious dis-optimisation, with 8-bytes per base class getting added!
The compressed_pair way would be:
struct C { private: boost::compressed_pair< empty_type1, boost::compressed_pair< empty_type2, non_empty_type> > m_data; };
and of course this way struct C doesn't have any "accidental" base classes, not even private ones.
But then C can't itself be an empty class, IIUC. Am I wrong about that? -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

"Joao Abecasis" <jpabecasis@zmail.pt> wrote in message news:413FD70A.4060801@zmail.pt... Hi! I have written a small utility class to provide "empty member optimization" for arbitrary types. The implementation makes use of the empty base class optimization (EBCO) but avoids injection of the class. A uniform and contained interface is exposed wether the optimization applies or not: template <typename T, size_t Index> compressed_pair { // several constructors may be provided but, for now, // default and copy constructor typedef T value_type; value_type & get(); value_type const & get() const; }; I believe this could be used as the basis for a compressed_tuple. Here's a sample use: template <typename T0, typename T1> struct A : private compressed_member<T0> , private compressed_member<T1, 1> { typedef compressed_member<T0> member0; typedef compressed_member<T1, 1> member1; T0 const & first() { return base0::get(); } T1 const & second() { return base1::get(); } }; There is a catch with the current implementation: some regular empty member (or regular empty member of some compressed_member) could end up sharing address with an empty compressed_member. This happens because compressed_member<T> does not inherit from T but hides this "relationship" from the compiler. Is there any interest in such an utility? To test compressed member I wrote another (toy) utility: empty_store. empty_store is an empty class with a static map that associates an instance's address to data. Checks are made everywhere to ensure that no two instances share the same address. The code can be found (zipped) here: *http://tinyurl.com/4e85l* Best regards, João Abecasis _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

"Joao Abecasis" <jpabecasis@zmail.pt> wrote in message news:
Hi!
Hi! First, I accidentally posted a reply which contained no new content.
I have written a small utility class to provide "empty member optimization" for arbitrary types. The implementation makes use of the empty base class optimization (EBCO) but avoids injection of the class. A uniform and contained interface is exposed wether the optimization applies or not:
<snip>
I believe this could be used as the basis for a compressed_tuple.
Second, I don't have time to look into the details of your implementation, but I wan't to make sure you're aware of the EBO problems Dave Held ran into with his policy_ptr, based on the loki SmartPtr. Basically, compilers will apply EBO once or twice for a given class, and that's it. The exception seems to be recent versions of gcc (I don't remember if its 3.3 or 3.4). If you haven't seen it, you should look at his utility optimally inherit, in the sandbox: http://tinyurl.com/4yyzs. (I apologize in advance if these remarks are way off base!) Jonathan
participants (4)
-
David Abrahams
-
Joao Abecasis
-
John Maddock
-
Jonathan Turkanis