Re: [boost] dangerous_cast

2002-12-07 Thread Gabriel Dos Reis

Hi,

Anthony Williams [EMAIL PROTECTED] writes:

[...]

|   You made youself clear.
|   
|   However, there are two running issues originating from a claim of Dave
|   that dangerous_cast might be better than reinterpret_cast in
|   casting from U* to T* (dangerous_cast uses the intermediate step
|   void* via static_cast).
|   
| 1) is dangerous_cast better than reinterpret_cast?
|   
| 2) is it well-defined to dereference the value obtained from
|   
|  U* - void* - T*
|   
|?
|   
|   You've showed that si U == char, (the case in Dave's example) then it
|   is well-formed.  The other cases are left undefined.
|   
|   So the key question (1) is still unanswered.
| 
| Well, given that we have a valid use when U==(unsigned) char, I think it is
| certainly better than reinterpret_cast in that case.

I returned back and checked the standard paragraph from which you
quoted decivise texts; you said:

  Also, 3.9.2p4 says: 
Objects of cvqualified (3.9.3) or cvunqualified type void* (pointer to
void), can be used to point to objects of unknown type. A void* shall be
able to hold any object pointer. A cvqualified or cvunqualified (3.9.3)
void* shall have the same representation and alignment requirements as a
cvqualified or cvunqualified char*.

  So casting a void* to/from a char* is a no-op.

I tend to find that quite conviincing (and agree with you), except
that it doesn't say that both pointer have the same *value*
representation as it explicitly says in a paragraph just before:

3.9.2/3
  [...] Pointers to cv-qualified and cv-unqualified versions (3.9.3)
  of layout-compatible types shall have the same value representation
  and align-ment requirements (3.9).


I'm not sure whether
  1) the difference in wordings is intended;
  2) really matters in the specific case we're discussing.


Now, let me attempt to address this key question I repeatedly asked

   Can reinterpret_castvoid*(foo) give a different from
   static_castvoid*(foo)?

I think the answer is simple: No.  The reason is that 

5.2.10/1:
  The result of the expression reinterpret_castT(v) is the result of
  converting the expression v to type T. [...]

and converting a Foo* to void* is well-defined.
Next, reinterpret_castchar*(foo) is well-defined as char* and void*
are intended to have the same memory-alias properties.

I hope, that is not word-game.

| However, for (2), it is only safe to dereference the resulting pointer if
| there is a T at the location that the final T* points to. This is true
| irrespective of what U is. However, there are very few cases in which you are
| guaranteed to be able to get a valid U* that holds a valid T* --- given that U
| and T may have different alignment requirements, and an implementation is
| permitted to drop any unnecessary info from pointers, so T* and U* may only
| store addresses which are valid multiples of the alignment for T and U
| respectively, so it is unlikely that you would get a valid case, unless there
| was special dispensation.
| 
| One of these is U==char or void, as I showed.
| 
| Another case to consider is when U is a POD-struct and T is the type of the
| first data member (or vice-versa). In this case, reinterpret_cast is
| guaranteed to work (9.2p17), so what about dangerous_cast? IMO, there is no
| guarantee, though I would be surprised if it didn't work. Indeed, I read the
| note on that paragraph to indicate that the intent is that the address is the
| same, and thus static_castvoid* will yield the same result. However, I can't
| find any normative guarantee.
| 
| A third case to consider is when T and U are the types of members of the same
| union. In this case, reinterpret_cast to a pointer to the union type and
| back is guaranteed (since the members are to be allocated as if they were the
| only member of a struct), and I would be surprised if it didn't work, but I
| can't find a normative guarantee.

I think I understand of the two issues you raised.  I'll try to
address them in a a different posting.

Thanks for your analysis and patience,

-- Gab
___
Unsubscribe  other changes: http://lists.boost.org/mailman/listinfo.cgi/boost



Re: [boost] dangerous_cast

2002-12-05 Thread Anthony Williams
Gabriel Dos Reis writes:
  Peter Dimov [EMAIL PROTECTED] writes:
  
  | From: Gabriel Dos Reis [EMAIL PROTECTED]
  |  David Abrahams [EMAIL PROTECTED] writes:
  | 
  |  | Gabriel Dos Reis [EMAIL PROTECTED] writes:
  |  |
  |  |  Hmm, I have a couple of questions answers to which will help me
  |  |  get your point.
  |  | 
  |  |   1) Why can't you do that with reinterpret_cast?
  |  |
  |  | You can, but the results are non-portable
  | 
  |  No more non-portable than with dangerous_cast.
  | 
  | I think that Dave's point is that in
  | 
  | new(h.storage) Foo;
  | 
  | there is a char* - void* implicit conversion as placement new takes a
  | void*. So the placement new performs char* - void* - Foo* (by constructing
  | a Foo at (void*)h.storage), which - in theory - might not be the same as
  | char* - Foo*.
  
  But then, that theoretical implementation can remember the type from
  which the conversion to void* was made and may issue an error when an
  attempt is made to dereference the pointer obtained by recasting the
  result of static_castvoid*(h.storage) to Foo*.
  
  Practical notes:
  Historically, char* was used as the type of raw memory until void*
  was invented.  And since then char* and void* continues to have the
  same properties as raw-memory issues are concerned.

unsigned char* has _additional_ properties to void* --- you can access the
object representation of _any_ object through an unsigned char* (and for PODs,
you can copy them around using this)

3.9p4:
  The object representation of an object of type T is the sequence of N
  unsigned char objects taken up by the object of type T, where N equals
  sizeof(T).

3.10p15:
  If a program attempts to access the stored value of an object through an
  lvalue of other than one of the following types the behavior is undefined:

  - the dynamic type of the object,

  ...

  - a char or unsigned char type.

So given a Foo object foo, static_castchar*(static_castvoid*(foo)) is
legal, and can be used to access the object representation of the object.

Also, 3.9.2p4 says: 
  Objects of cvqualified (3.9.3) or cvunqualified type void* (pointer to
  void), can be used to point to objects of unknown type. A void* shall be
  able to hold any object pointer. A cvqualified or cvunqualified (3.9.3)
  void* shall have the same representation and alignment requirements as a
  cvqualified or cvunqualified char*.

So casting a void* to/from a char* is a no-op.

3.8p1:
  The lifetime of an object is a runtime property of the object. The lifetime
  of an object of type T begins when:

  - storage with the proper alignment and size for type T is obtained, and

  - if T is a class type with a nontrivial constructor (12.1), the constructor
  call has completed.

Thus, given that h.storage is properly aligned, (which is the purpose of the
other union member), after new(h.storage) Foo, h.storage contains a Foo
object. Thus accessing it through a pointer-to-Foo is legal, as Foo is the
dynamic type of the object.

The question is: is the Foo* returned by the placement new expression (which
is usable) the same as the Foo* returned from
static_castFoo*(static_castvoid*(h.storage))?

The object representation of the Foo object is the sizeof(T) bytes starting at
static_castvoid*(h.storage) (since that is what was passed to placement
new), so static_castvoid*(pfoo)==static_castvoid*(h.storage) if pfoo is
the value returned from the new expression.

Thus

static_castFoo*(static_castvoid*(pfoo))
==static_castFoo*(static_castvoid*(h.storage))
==pfoo

and we're legal.

Anthony
-- 
Anthony Williams
Senior Software Engineer, Beran Instruments Ltd.
Remove NOSPAM when replying, for timely response.

___
Unsubscribe  other changes: http://lists.boost.org/mailman/listinfo.cgi/boost



Re: [boost] dangerous_cast

2002-12-05 Thread Anthony Williams
Gabriel Dos Reis writes:
  Hi,

Hi

  Anthony Williams [EMAIL PROTECTED] writes:
  
  [...]
  
  | 3.10p15:
  |   If a program attempts to access the stored value of an object through an
  |   lvalue of other than one of the following types the behavior is undefined:
  | 
  |   - the dynamic type of the object,
  | 
  |   ...
  | 
  |   - a char or unsigned char type.
  | 
  | So given a Foo object foo, static_castchar*(static_castvoid*(foo)) is
  | legal, and can be used to access the object representation of the object.
  
  There is no question that the above cast is legal.  I thin the issue
  is elsewhere.  The key question is whether that may be different from
  
 reinterpret_castvoid*(foo);

I thought the issue was whether the pair of static_casts in dangerous_cast
was as implementation defined as a reintepret_cast would be. If you read my
mail to the end, hopefully I have explained that I think that the
static_cast pair is legal and well-defined, as opposed to using
reinterpret_cast, which is implementation-defined. If I haven't made myself
clear, I apologise, and will try again.

Anthony
-- 
Anthony Williams
Senior Software Engineer, Beran Instruments Ltd.
Remove NOSPAM when replying, for timely response.

___
Unsubscribe  other changes: http://lists.boost.org/mailman/listinfo.cgi/boost



Re: [boost] dangerous_cast

2002-12-05 Thread Anthony Williams
Gabriel Dos Reis writes:
  Anthony Williams [EMAIL PROTECTED] writes:
  
  |   Anthony Williams [EMAIL PROTECTED] writes:
  |   
  |   [...]
  |   
  |   | 3.10p15:
  |   |   If a program attempts to access the stored value of an object through an
  |   |   lvalue of other than one of the following types the behavior is undefined:
  |   | 
  |   |   - the dynamic type of the object,
  |   | 
  |   |   ...
  |   | 
  |   |   - a char or unsigned char type.
  |   | 
  |   | So given a Foo object foo, static_castchar*(static_castvoid*(foo)) is
  |   | legal, and can be used to access the object representation of the object.
  |   
  |   There is no question that the above cast is legal.  I thin the issue
  |   is elsewhere.  The key question is whether that may be different from
  |   
  |  reinterpret_castvoid*(foo);
  | 
  | I thought the issue was whether the pair of static_casts in dangerous_cast
  | was as implementation defined as a reintepret_cast would be. If you read my
  | mail to the end, hopefully I have explained that I think that the
  | static_cast pair is legal and well-defined, as opposed to using
  | reinterpret_cast, which is implementation-defined. If I haven't made myself
  | clear, I apologise, and will try again.
  
  You made youself clear.
  
  However, there are two running issues originating from a claim of Dave
  that dangerous_cast might be better than reinterpret_cast in
  casting from U* to T* (dangerous_cast uses the intermediate step
  void* via static_cast).
  
1) is dangerous_cast better than reinterpret_cast?
  
2) is it well-defined to dereference the value obtained from
  
 U* - void* - T*
  
   ?
  
  You've showed that si U == char, (the case in Dave's example) then it
  is well-formed.  The other cases are left undefined.
  
  So the key question (1) is still unanswered.

Well, given that we have a valid use when U==(unsigned) char, I think it is
certainly better than reinterpret_cast in that case.

However, for (2), it is only safe to dereference the resulting pointer if
there is a T at the location that the final T* points to. This is true
irrespective of what U is. However, there are very few cases in which you are
guaranteed to be able to get a valid U* that holds a valid T* --- given that U
and T may have different alignment requirements, and an implementation is
permitted to drop any unnecessary info from pointers, so T* and U* may only
store addresses which are valid multiples of the alignment for T and U
respectively, so it is unlikely that you would get a valid case, unless there
was special dispensation.

One of these is U==char or void, as I showed.

Another case to consider is when U is a POD-struct and T is the type of the
first data member (or vice-versa). In this case, reinterpret_cast is
guaranteed to work (9.2p17), so what about dangerous_cast? IMO, there is no
guarantee, though I would be surprised if it didn't work. Indeed, I read the
note on that paragraph to indicate that the intent is that the address is the
same, and thus static_castvoid* will yield the same result. However, I can't
find any normative guarantee.

A third case to consider is when T and U are the types of members of the same
union. In this case, reinterpret_cast to a pointer to the union type and
back is guaranteed (since the members are to be allocated as if they were the
only member of a struct), and I would be surprised if it didn't work, but I
can't find a normative guarantee.

If these last two cases are guaranteed to be OK, then I think we have
sufficient to indicate that dangerous_cast can be useful. If not, then it
ought to be renamed, and only defined for char types.

Anthony
-- 
Anthony Williams
Senior Software Engineer, Beran Instruments Ltd.
Remove NOSPAM when replying, for timely response.

___
Unsubscribe  other changes: http://lists.boost.org/mailman/listinfo.cgi/boost



Re: [boost] dangerous_cast

2002-12-04 Thread David Abrahams
David B. Held [EMAIL PROTECTED] writes:

 Does this have any merit whatsoever?

 template typename T, typename U
 T* dangerous_cast(U* p)
 {
 return static_castT*(static_castvoid*(p));
 }

 Obviously, you only use it when you know the binary layout of some
 object(s) and no other cast produces defined or reliable behaviour.
 Of course, I'm not sure this produces either of those either. :(

It's better than reinterpret_cast for most purposes. Not sure about
the name. 


how-about-hellfire_breathing_cast-ly y'rs,
-- 
   David Abrahams
   [EMAIL PROTECTED] * http://www.boost-consulting.com
Boost support, enhancements, training, and commercial distribution

___
Unsubscribe  other changes: http://lists.boost.org/mailman/listinfo.cgi/boost



Re: [boost] dangerous_cast

2002-12-04 Thread Gabriel Dos Reis
David B. Held [EMAIL PROTECTED] writes:

| Does this have any merit whatsoever?
| 
| template typename T, typename U
| T* dangerous_cast(U* p)
| {
| return static_castT*(static_castvoid*(p));
| }

Is it any different from reinterpret_castT*(p) ?

-- Gaby
___
Unsubscribe  other changes: http://lists.boost.org/mailman/listinfo.cgi/boost



Re: [boost] dangerous_cast

2002-12-04 Thread Gabriel Dos Reis
David Abrahams [EMAIL PROTECTED] writes:

| Gabriel Dos Reis [EMAIL PROTECTED] writes:
| 
|  David B. Held [EMAIL PROTECTED] writes:
| 
|  | Does this have any merit whatsoever?
|  | 
|  | template typename T, typename U
|  | T* dangerous_cast(U* p)
|  | {
|  | return static_castT*(static_castvoid*(p));
|  | }
| 
|  Is it any different from reinterpret_castT*(p) ?
| 
| It might be, depending on your compiler. The behavior of
| reinterpret_castT* is implementation-defined.

The mapping used by reinterpret_cast is implementation defined,
thus an implementation is required to document it.

 1) Do you have any corresponding guarantee for converting a void* to
T* when the source is *not* a T* nor the address obtained from a T?

 2) Isn't the mapping U* - void* - T* at best
implementation-defined if not undefined behaviour? 

-- Gaby
___
Unsubscribe  other changes: http://lists.boost.org/mailman/listinfo.cgi/boost



Re: [boost] dangerous_cast

2002-12-04 Thread Terje Slettebø
From: David Abrahams [EMAIL PROTECTED]

 Gabriel Dos Reis [EMAIL PROTECTED] writes:

  David B. Held [EMAIL PROTECTED] writes:
 
  | Does this have any merit whatsoever?
  |
  | template typename T, typename U
  | T* dangerous_cast(U* p)
  | {
  | return static_castT*(static_castvoid*(p));
  | }
 
  Is it any different from reinterpret_castT*(p) ?

 It might be, depending on your compiler. The behavior of
 reinterpret_castT* is implementation-defined.

Doesn't that mean that dangerous_cast would also be implementation-defined?
:)


Regards,

Terje

___
Unsubscribe  other changes: http://lists.boost.org/mailman/listinfo.cgi/boost



Re: [boost] dangerous_cast

2002-12-04 Thread Terje Slettebø
From: Terje Slettebø [EMAIL PROTECTED]

 From: David Abrahams [EMAIL PROTECTED]

  Gabriel Dos Reis [EMAIL PROTECTED] writes:
 
   Is it any different from reinterpret_castT*(p) ?
 
  It might be, depending on your compiler. The behavior of
  reinterpret_castT* is implementation-defined.

 Doesn't that mean that dangerous_cast would also be
implementation-defined?

Oops, never mind. I thought you meant that the behaviour of dangerous_cast
would depend on the compiler, but I understand now that you meant
reinterpret_cast.

Terje

___
Unsubscribe  other changes: http://lists.boost.org/mailman/listinfo.cgi/boost



Re: [boost] dangerous_cast

2002-12-04 Thread David Abrahams
Gabriel Dos Reis [EMAIL PROTECTED] writes:

 David Abrahams [EMAIL PROTECTED] writes:

 | Gabriel Dos Reis [EMAIL PROTECTED] writes:
 | 
 |  David B. Held [EMAIL PROTECTED] writes:
 | 
 |  | Does this have any merit whatsoever?
 |  | 
 |  | template typename T, typename U
 |  | T* dangerous_cast(U* p)
 |  | {
 |  | return static_castT*(static_castvoid*(p));
 |  | }
 | 
 |  Is it any different from reinterpret_castT*(p) ?
 | 
 | It might be, depending on your compiler. The behavior of
 | reinterpret_castT* is implementation-defined.

 The mapping used by reinterpret_cast is implementation defined,
 thus an implementation is required to document it.

  1) Do you have any corresponding guarantee for converting a void* to
 T* when the source is *not* a T* nor the address obtained from a T?

  2) Isn't the mapping U* - void* - T* at best
 implementation-defined if not undefined behaviour? 

template class T
struct holder
{
   union {
  aligned_PODT::type aligner;
  char storage[sizeof(T)];
   };
};

holderFoo h;
new (h.storage) Foo;
   ...
T* u = dangerous_castT*(h.storage); // can't do this with reinterpret_cast

HTH,
-- 
   David Abrahams
   [EMAIL PROTECTED] * http://www.boost-consulting.com
Boost support, enhancements, training, and commercial distribution

___
Unsubscribe  other changes: http://lists.boost.org/mailman/listinfo.cgi/boost



Re: [boost] dangerous_cast

2002-12-04 Thread Gabriel Dos Reis
David Abrahams [EMAIL PROTECTED] writes:

| Gabriel Dos Reis [EMAIL PROTECTED] writes:
| 
|  Hmm, I have a couple of questions answers to which will help me
|  get your point.
| 
|   1) Why can't you do that with reinterpret_cast?
| 
| You can, but the results are non-portable

No more non-portable than with dangerous_cast.

|   2) Why is that supposed to be better than reinterpret_cast?
| 
| The results are portable

Please, walk me through the details.

-- Gaby
___
Unsubscribe  other changes: http://lists.boost.org/mailman/listinfo.cgi/boost



Re: [boost] dangerous_cast

2002-12-04 Thread Gabriel Dos Reis
Peter Dimov [EMAIL PROTECTED] writes:

| From: Gabriel Dos Reis [EMAIL PROTECTED]
|  David Abrahams [EMAIL PROTECTED] writes:
| 
|  | Gabriel Dos Reis [EMAIL PROTECTED] writes:
|  |
|  |  Hmm, I have a couple of questions answers to which will help me
|  |  get your point.
|  | 
|  |   1) Why can't you do that with reinterpret_cast?
|  |
|  | You can, but the results are non-portable
| 
|  No more non-portable than with dangerous_cast.
| 
| I think that Dave's point is that in
| 
| new(h.storage) Foo;
| 
| there is a char* - void* implicit conversion as placement new takes a
| void*. So the placement new performs char* - void* - Foo* (by constructing
| a Foo at (void*)h.storage), which - in theory - might not be the same as
| char* - Foo*.

But then, that theoretical implementation can remember the type from
which the conversion to void* was made and may issue an error when an
attempt is made to dereference the pointer obtained by recasting the
result of static_castvoid*(h.storage) to Foo*.

Practical notes:
Historically, char* was used as the type of raw memory until void*
was invented.  And since then char* and void* continues to have the
same properties as raw-memory issues are concerned.


-- Gaby

___
Unsubscribe  other changes: http://lists.boost.org/mailman/listinfo.cgi/boost