Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

reinterpret_cast<char*>(p) or static_cast<char*>((void*)p)) for bytewise pointer difference, which is better?

Is there any difference between the following three casts for extracting raw byte pointers for use in pointer arithmetic? (assume a platform where char is 1 byte.)

  1. static_cast<char*>((void*)ptr))
  2. reinterpret_cast<char*>(ptr)
  3. (updated) or: static_cast<char*>(static_cast<void*>(ptr))

Which should I prefer?

In more detail...

Given pointers to two member objects in a class, I would like to compute an offset from one to the other, so that I can reconstruct the address of one member given an offset and the address of the other member.

// assumed data layout:
struct C {
  // ...
  A a;
  // ...
  B b;
}

The code that I use at the moment is along the lines of:

void approach1( A *pa, B *pb )
{
  // compute offset:
  std::ptrdiff_t offset = static_cast<char*>((void*)pa) - static_cast<char*>((void*)pb);
  // then in some other function...
  // given offset and ptr to b, compute ptr to a:
  A *a = static_cast<A*>( (void*)(static_cast<char*>((void*)pb) + offset) );
}

main()
{
  C c;
  approach1(&c.a, &c.b);
}

I would like to know whether the following is better (or worse):

void approach2( A *pa, B *pb )
{
  std::ptrdiff_t offset = reinterpret_cast<char*>(pa) - reinterpret_cast<char*>(pb);
  // ...
  A *a = reinterpret_cast<A*>( reinterpret_cast<char*>(pb) + offset );
}

Are the two methods entirely equivalent? Are they equally portable?

My impression is that approach1() is more portable, because "static_casting a pointer to and from void* preserves the address," whereas reinterpret_cast<> guarantees less (see accepted answer at link).

I would like to know what the cleanest way to do this is.

Update: Explanation of Purpose

A number of people have asked what is the purpose of computing these offsets. The purpose is to construct a meta-class table of instance offsets. This is used by a runtime reflection mechanism for automatic GUI building and persistance (the offsets are not serialized, just used to traverse the structure). The code has been in production for over 15 years. For the purposes of this question I just want to know the most portable way of computing the pointer offsets. I have no intention of making large changes to the way the metaclass system works. In addition, I'm also generally interested in the best way to do this, as I have other uses in mind (e.g. difference pointers for shared memory code).

NOTE: I can not use offsetof() because in my actual code I only have the pointers to instances a and b, I don't necessarily have the type of the containing object c or other static info to use offsetof(). All I can assume is that a and b are members of the same object.

like image 782
Ross Bencina Avatar asked Sep 03 '14 07:09

Ross Bencina


People also ask

What is reinterpret_cast char *> in C++?

reinterpret_cast is a type of casting operator used in C++. It is used to convert a pointer of some data type into a pointer of another data type, even if the data types before and after conversion are different. It does not check if the pointer type and data pointed by the pointer is same or not.

Is reinterpret_cast safe?

The result of a reinterpret_cast cannot safely be used for anything other than being cast back to its original type. Other uses are, at best, nonportable. The reinterpret_cast operator cannot cast away the const , volatile , or __unaligned attributes.

Is reinterpret_cast undefined Behaviour?

Although the reinterpret_cast itself might be unspecified behaviour, attempting to access the parameters once you've done the cast is undefined behaviour.


1 Answers

These two will lead to the same result so the difference is mostly semantical, and reinterpret_cast has exactly the meaning of the operation you want, plus the fact that only one cast is required instead of two (and the less cast you have in your code the better).

reinterpret_cast

5.2.10/7: An object pointer can be explicitly converted to an object pointer of a different type. When a prvalue v of object pointer type is converted to the object pointer type “pointer to cv T”, the result is static_cast< cv T* >(static_cast< cv void* >(v)).

So except if an exotique random low level different behaviour appears on a middle-age platform, you should definitely go with:

reinterpret_cast<char*>(ptr);

In general.

That said, why don't you use uintptr_t in your case ? it's even more apropriate, you need no pointer:

void approach3( A *pa, B *pb )
{
  std::ptrdiff_t offset = reinterpret_cast<std::uintptr_t>(pa) - reinterpret_cast<std::uintptr_t>(pb);
  // ...
  A *a = reinterpret_cast<A*>( reinterpret_cast<std::uintptr_t>(pb) + offset );
}

For additional information see:

http://en.cppreference.com/w/cpp/language/reinterpret_cast

like image 104
Drax Avatar answered Sep 29 '22 13:09

Drax