Re: "struct hack" in C++0x?
{ Please fit your text within 80 columns, preferably 70 or so.
Unintended line breaks makes the code hard to read, and even to compile,
especially with comments. -mod }
On Jun 8, 10:09 am, Olivier <olivier.gr...@gmail.com> wrote:
Practically, the religious flaminess excluding the struct hack from C+
+ specific programming, is that you can't use operator new on a struct
struct_hack using the struct hack. As in C, you're stuck with malloc/
calloc of a single struct_hack at a time, because of the near-
meaningless of sizeof(struct_hack). (In practice, a useful choice of
trailing array size is zero. But this is not accurate for any useful
instance.)
What about the following code which pretty much does the same thing :
// base structure.
struct hack
{
int i;
char c;
int array[1];
};
// extended structure.
template< std::size_t I >
struct hack_ext
{
int i;
char c;
int array[I];
};
int main( )
{
hack *hack_object = reinterpret_cast<hack *>(new hack_ext<10>);
hack_object->i = 0;
hack_object->c = 'c';
for(int i=0; i<10; i++) hack_object->array[i] = i;
return 0;
}
As far as I recall, the standard guarantees that structures starting
with the same data members will identically map to memory. I know that
reinterpret_cast is not portable in absolute, but in this particular
case (and if that guarantee is not the fruit of my imagination),
shouldn't this always work and actually be portable ?
I was meaning to post this counter-reply, but you beat me to it.
However, while thinking about it, I realized there is a strictly
conforming C++03 analog using templates, but yours is not it. The
intent of the C++03 standard is pretty clear to allow
reinterpret_cast'ing between "pointers to two different POD structs"
and accessing the common leading part, if any. \However\, your example
is not strictly conforming. You are accessing hack::array[5] which is
not part of the struct, and thus not part of the "common leading
part", and thus undefined behavior. My initial thought would be to
reinterpret_cast it to a pointer to a hack_ext<max_value_siz_t>, then
access the common leading part. This almost works, but the problem
remains on how to create such things. You cannot create a template
struct with a template argument of a runtime value. Specifically, your
code "new hack_ext<10>" works only because 10 is a const-expr. For
example, implementing clone (efficiency) would be impossible.
However, I think to implement the functionality and be strictly
conforming is still possible with judicious use of placement new and a
slightly different interface. I am, however, making an assumption
regarding alignment which is not guaranteed by the standard, that a
type may be aligned on multiples of its size.
I believe the code below is relying on a conservative reading of the C+
+03 standard, including round trip conversions from T* to char* and
back to the same T*.
DISCLAIMER !! not thoroughly tested
#include <iostream>
#include <cstdlib>
#include <new>
using namespace std;
//interface
template <typename T> struct c_struct_hack
{
private:
//Alignment assumption isolated here.
//Assuming that a type's alignment is a multiple of its size.
static size_t const offset_to_array = ((sizeof(size_t) >= sizeof(T)) ? sizeof(size_t) : sizeof(T));
public:
static c_struct_hack* create(size_t const size)
{ char * const c = static_cast<char*>(::operator new(offset_to_array + size * sizeof(T)));
try
{ c_struct_hack * const hack_ptr = new (c) c_struct_hack(size);
try
{ new (c + offset_to_array) T[size];
return hack_ptr;
} catch (...)
{ hack_ptr->~c_struct_hack(); //is this necessary? probably no
throw;
}
} catch (...)
{ ::operator delete(c);
throw;
}
}
static void destroy(c_struct_hack const* h) throw()
{ size_t const size = h->size_;
h->~c_struct_hack(); //is this necessary? probably no
T const* const array_start = reinterpret_cast<T const*>(reinterpret_cast<char const*>(h) + offset_to_array);
T const* array_end = reinterpret_cast<T const*>(reinterpret_cast<char const*>(h) + offset_to_array) + size;
for (;;)
{ if (array_start == array_end)
break;
--array_end;
array_end->~T();
}
::operator delete(const_cast<void*>(static_cast<void const*>(h)));
}
size_t size() const throw() { return size_; }
T& operator[] (size_t pos) throw()
{ return reinterpret_cast<T*>(reinterpret_cast<char*>(this) + offset_to_array)[pos]; }
T const& operator[] (size_t pos) const throw()
{ return reinterpret_cast<T const*>(reinterpret_cast<char const*>(this) + offset_to_array)[pos]; }
private:
size_t const size_;
c_struct_hack(size_t arg_size) : size_(arg_size) {} //private to impl
~c_struct_hack() {} //private to impl
c_struct_hack(c_struct_hack const& ); //not defined
c_struct_hack& operator=(c_struct_hack const& );//not defined
};
struct foo { int x; };
int main()
{
{
c_struct_hack<foo> * h = c_struct_hack<foo>::create(5);
foo& f = (*h)[4];
f.x = 5;
c_struct_hack<foo>::destroy(h);
}
{
c_struct_hack<int> * h = c_struct_hack<int>::create(5);
(*h)[4] = 3;
c_struct_hack<int> const * h2 = h;
int tmp = (*h2)[4];
c_struct_hack<int>::destroy(h2);
return tmp;
}
}
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]