Best way to cast anythign to void* and back

  • Thread starter christopher diggins
  • Start date
P

Pete Becker

christopher said:
char[sizeof(T)] pchar;
if (sizeof(x) <= sizeof(pvoid)) {
new(pvoid) T(x);

'pvoid' was supposed to be 'pchar' in both places, right? Doesn't help,
for essentially the same reason: there are no contraints on the
alignment of char objects, so pchar is not necessarily suitably aligned
for T.

What you could do (and this is pretty ugly) is to create a union of some
broad set of builtin types, and assume that objects of that type will
also be suitably aligned for user-defined types.
 
M

Mike Wahler

I don't know how common it might be, but when I used
that acronym, I meant 'implementation defined behavior'.

-Mike
 
C

christopher diggins

Pete Becker said:
christopher said:
char[sizeof(T)] pchar;
if (sizeof(x) <= sizeof(pvoid)) {
new(pvoid) T(x);

'pvoid' was supposed to be 'pchar' in both places, right?
Yes.

Doesn't help, for essentially the same reason: there are no contraints on
the alignment of char objects, so pchar is not necessarily suitably
aligned for T.

I see. If we revisit the original code:

void* pvoid;
if (sizeof(x) <= sizeof(pvoid)) {
new(pvoid) T(x);

would the new operation not simply throw an exception if the type can not be
aligned, correct?

Thanks again for all your help Pete, I really appreciate it!

Christopher Diggins
http://www.cdiggins.com
 
P

Pete Becker

christopher said:
I see. If we revisit the original code:

void* pvoid;
if (sizeof(x) <= sizeof(pvoid)) {
new(pvoid) T(x);

would the new operation not simply throw an exception if the type can not be
aligned, correct?

It might, but there's no such requirement.

Placement new is pretty simple:

void *operator new(size_t, void *addr)
{
return addr;
}

No exceptions there. The constructor might do something that triggers an
exception, but again there's no such requirement.

Alignment problems often show up as bus errors. Sometimes they just make
the code run slower.
 
C

christopher diggins

Pete Becker said:
It might, but there's no such requirement.

Placement new is pretty simple:

void *operator new(size_t, void *addr)
{
return addr;
}

No exceptions there. The constructor might do something that triggers an
exception, but again there's no such requirement.

Alignment problems often show up as bus errors. Sometimes they just make
the code run slower.

Hmmm ... still no good news. :p

But the implementation you provided seems to violate the standard though,
specifically:

3.7.3.1/2 Allocation functions

"The pointer returned shall be suitably aligned so that it can be converted
to a
pointer of any complete object type and then used to access the object or
array in the storage allocated"

There is also:

5.3.4/10 New

"For arrays of char and unsigned char, the difference between the result of
the new-expression and the address returned by the allocation function shall
be an integral multiple of the most stringent alignment requirement (3.9) of
any object type whose size is no greater than the size of the array being
created."

These requirements seem to imply that it is the STL's responsibility to
return a pointer to a suitably aligned object even when using placement new?
Simply returning a void* seems insufficient.

I would have thought at the very least the standard implies that I can rely
on the following:

assert(sizeof(T) <= sizeof(void*));
void* pvoid;
bool misaligned = (new(&pvoid) T() != &pvoid);
if (misaligned) throw runtime_error("what an evil platform you have
chosen");

Any thoughts?
 
P

Pete Becker

christopher said:
But the implementation you provided seems to violate the standard though,
specifically:

3.7.3.1/2 Allocation functions

18.4.1.3 (Placement forms of operator new and operator delete): "The
provisions of 3.7.3 do not apply to these reserved placement forms of
operator new and operator delete."

And that's the whole point of having them: the user is supplying the
memory, and presumably knows what's needed.
 
M

msalters

christopher diggins schreef:
Hello,

I want to convert a value of type T where sizeof(T) <= sizeof(void*) into a
void* and back again.

const int N = sizeof(void*);
char* buf = new char[N]; // aligned for T
new (buf) T (src); // copy source value into buf
void* dest;
std::copy( buf,buf+N, reinterpret_cast<char*>( &dest ) );

gets you nearly there. However, looking at dest is UB. It may contain
padding bits. That is the main reason you can't use them as char[]s.

HTH,
Michiel Salters
 
C

christopher diggins

Pete Becker said:
18.4.1.3 (Placement forms of operator new and operator delete): "The
provisions of 3.7.3 do not apply to these reserved placement forms of
operator new and operator delete."

Grumble grumble. Darn it.
And that's the whole point of having them: the user is supplying the
memory, and presumably knows what's needed.

Okay I think I have a solution: char buffers and memcpy!

3.9/2 says:

For any complete POD object type T, whether or not the object holds a valid
value of type T, the underlying
bytes (1.7) making up the object can be copied into an array of char or
unsigned char.36) If the content

of the array of char or unsigned char is copied back into the object, the
object shall subsequently

hold its original value. [Example:

#define N sizeof(T)

char buf[N];

T obj; // obj initialized to its original value

memcpy(buf, &obj, N); // between these two calls to memcpy,

// obj might be modified

memcpy(&obj, buf, N); // at this point, each subobject of obj of scalar type

// holds its original value

-end example]

So:

This restricts the examples I have been working with to also be "POD" types,
and the storage to be a character buffer, but these restrictions I can live
with.

Thanks ever so much for all your help Pete! Let me know if you ever need a
favour.

- Christopher Diggins
 
C

christopher diggins

msalters said:
christopher diggins schreef:
Hello,

I want to convert a value of type T where sizeof(T) <= sizeof(void*) into
a
void* and back again.

const int N = sizeof(void*);
char* buf = new char[N]; // aligned for T
new (buf) T (src); // copy source value into buf
void* dest;
std::copy( buf,buf+N, reinterpret_cast<char*>( &dest ) );

gets you nearly there. However, looking at dest is UB. It may contain
padding bits. That is the main reason you can't use them as char[]s.

HTH,
Michiel Salters

Thanks a lot Michiel!
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,997
Messages
2,570,239
Members
46,827
Latest member
DMUK_Beginner

Latest Threads

Top