Re: Zero-size array as struct member
On Aug 27, 1:56 pm, "joe" <jc1...@att.net> wrote:
Juha Nieminen wrote:
gwowen <gwo...@gmail.com> wrote:
On Aug 27, 11:58 am, Juha Nieminen <nos...@thanks.invalid> wrote:
Wrong? C++ does not offer tools to abstract the struct hack and make
it easier and safer to use?
We'll have to disagree, then.
The struct hack gives me a storage region of run-time-variable size,
contiguous with a place to store that size itself. Which C++
container gives you that? How would you abstract that requirement.
"C++ offers you tools" does not mean "C++ offers you a standard
container".
C++ offers tools for you to build your own abstract containers which
internally use the struct hack, but from the outside are easier and
safer to use than using the raw struct hack directly.
Such tools include classes (with public and private sections),
constructors, copy constructors, assignment operator overloading,
destructors, the RAII mechanism, and templates.
In other words, instead of using a raw struct pointer directly (whic=
h
points to a memory block which has been "overallocated" for the
purposes
of the struct hack), you use an abstract object which resembles a
smart pointer and which hides and automatizes the low-level dirty
details inside, making the usage of the struct hack easier and safer.
OK. I thought you were saying originally above that C++ offered an
alternative to the struct hack (because you have been on the track that
something with a std::vector in it was the "same thing"). The "same ol"
still applies though: because C++ does not recognize the struct hack as a
viable technique (like C99 does), you're still (and I do, do this)
wrapping a hack that may be compiler-specific or perhaps not guaranteed
to work. And also, it's hackish because in certain forms you loose the
ability to derive from such a wrapper, etc, and is therefore a
dialect-style of C++ programming.
I don't think you could ever derive from a struct hack struct in C++.
The data trails off past the end of the struct hack struct, and if you
derive from the struct and add new data members, then those data
members will exist in the "off the end" hack part from the base class.
This makes no sense to me offhand.
There is a way to do the equivalent of the struct hack in a fully
conforming way in C90 and C++03. It requires some slightly different
notation for use, but it has the same performance and layout. What
follows is some mostly untested code of the idea for C++03. I'm not
entirely sure it's correct, and while it does use some creative
casting hackery, I think that it is perfectly conforming code which is
guaranteed by standard to do what is intended, and it should work on
all reasonable implementations as well.
#ifndef SAFE_STRUCT_HACK_HPP_INCLUDE
#define SAFE_STRUCT_HACK_HPP_INCLUDE
#include <cstdlib>
using std::size_t; //because I'm lazy for this sample
// Public Interface
template <typename prefix_t, typename element_t>
struct safe_struct_hack_t; //Type not defined. It's an opaque type.
template <typename prefix_t, typename element_t>
inline
safe_struct_hack_t<prefix_t, element_t>*
safe_struct_hack_allocate(
size_t num_elements);
template <typename prefix_t, typename element_t>
inline
void
deallocate(
safe_struct_hack_t<prefix_t, element_t> const* );
template <typename prefix_t, typename element_t>
inline
prefix_t*
get_prefix(
safe_struct_hack_t<prefix_t, element_t>* );
template <typename prefix_t, typename element_t>
inline
prefix_t const*
get_prefix(
safe_struct_hack_t<prefix_t, element_t> const* );
template <typename prefix_t, typename element_t>
inline
size_t
get_num_elements(
safe_struct_hack_t<prefix_t, element_t>const * );
template <typename prefix_t, typename element_t>
inline
element_t*
get_element(
safe_struct_hack_t<prefix_t, element_t>* , size_t index);
template <typename prefix_t, typename element_t>
inline
element_t const*
get_element(
safe_struct_hack_t<prefix_t, element_t> const* , size_t
index);
//Private Implementation follows.
//Do not use or depend on implementation details!
//Overly conservative, but it should work portably
//for alignment. Right?
template <size_t x, size_t y>
struct roundup_x_to_nearest_multiple_of_y
{ enum { remainder = (x % y) };
enum { result = (remainder == 0) ? (x) : (x + (y - remainder)) };
};
template <typename prefix_t, typename element_t>
struct safe_struct_hack_offset_to_num_elements
{ enum { x = roundup_x_to_nearest_multiple_of_y<sizeof(prefix_t),
sizeof(size_t)>::result };
};
template <typename prefix_t, typename element_t>
struct safe_struct_hack_offset_to_first_element
{ enum { x =
roundup_x_to_nearest_multiple_of_y<
safe_struct_hack_offset_to_num_elements<prefix_t,
element_t>::x + sizeof(size_t),
sizeof(element_t)
>::result
};
};
template <typename prefix_t, typename element_t>
inline
safe_struct_hack_t<prefix_t, element_t>*
safe_struct_hack_allocate(
size_t num_elements)
{ size_t const allocated_size =
safe_struct_hack_offset_to_first_element<prefix_t,
element_t>::x
+ num_elements * sizeof(element_t);
char* c = new char[allocated_size];
safe_struct_hack_t<prefix_t, element_t>* const x =
reinterpret_cast<safe_struct_hack_t<prefix_t,
element_t>*>(c);
try
{ new (c) prefix_t;
try
{ new (c + safe_struct_hack_offset_to_num_elements<prefix_t,
element_t>::x) size_t(num_elements);
size_t i=0;
try
{ for ( ; i < num_elements; ++i)
new (get_element(x, i)) element_t();
} catch (...)
{ for (;;)
{ if (i == 0)
break;
--i;
get_element(x, i) -> ~element_t();
}
throw;
}
} catch (...)
{ reinterpret_cast<prefix_t*>(c) -> ~prefix_t();
throw;
}
} catch (...)
{ delete[] c;
throw;
}
return x;
}
template <typename prefix_t, typename element_t>
inline
void
deallocate(
safe_struct_hack_t<prefix_t, element_t> const* x)
{ reinterpret_cast<prefix_t const*>(x) -> ~prefix_t();
size_t const num_elements = get_num_elements(x);
for (size_t i=0; i<num_elements; ++i)
get_element(x, i) -> ~element_t();
delete[] reinterpret_cast<char const*>(x);
}
template <typename prefix_t, typename element_t>
inline
prefix_t*
get_prefix(
safe_struct_hack_t<prefix_t, element_t>* x)
{ return reinterpret_cast<prefix_t*>(x);
}
template <typename prefix_t, typename element_t>
inline
prefix_t const*
get_prefix(
safe_struct_hack_t<prefix_t, element_t> const* x)
{ return reinterpret_cast<prefix_t const*>(x);
}
template <typename prefix_t, typename element_t>
inline
size_t
get_num_elements(
safe_struct_hack_t<prefix_t, element_t>const * x)
{ char const* c =
reinterpret_cast<char const*>(x)
+ safe_struct_hack_offset_to_num_elements<prefix_t,
element_t>::x;
return * reinterpret_cast<size_t const*>(c);
}
template <typename prefix_t, typename element_t>
inline
element_t*
get_element(
safe_struct_hack_t<prefix_t, element_t>* x, size_t index)
{ char* ptr_to_first_ele =
reinterpret_cast<char*>(x);
+ safe_struct_hack_offset_to_first_element<prefix_t,
element_t>::x;
return reinterpret_cast<element_t*>(ptr_to_first_ele) + index;
}
template <typename prefix_t, typename element_t>
inline
element_t const*
get_element(
safe_struct_hack_t<prefix_t, element_t> const* x, size_t
index)
{ char const* ptr_to_first_ele =
reinterpret_cast<char const*>(x);
+ safe_struct_hack_offset_to_first_element<prefix_t,
element_t>::x;
return reinterpret_cast<element_t const*>(ptr_to_first_ele) +
index;
}
#endif