8

I have seen and used this many times is C++, specially in various thread implementations. What I wonder is if there are any pitfalls/ issues of doing this? Is there any way that we could run in to an error or undefined condition when we are casting to void* and back again? How should we resolve such issues if there are any?

Thank you.

Izza
  • 2,389
  • 8
  • 38
  • 60
  • 1
    "How should we resolve such issues if there are any?" don't use `void*`... – Luchian Grigore Sep 05 '12 at 06:02
  • `void*` is quite a powerful construct, but just as dangerous because there is just no way to tell why something goes wrong. you just have to be VERY careful when you use it. – Andreas Grapentin Sep 05 '12 at 06:06
  • Could you give an example (code) where you have seen this? I can think of cases where you have to do it that way. But as a general rule, avoid using void* where possible. – Axel Sep 05 '12 at 06:07
  • These days, you can usually use the standard thread library (or Boost's similar library if that's not available) rather than "various thread implementations". That doesn't require any dodgy pointer conversions in user code. – Mike Seymour Sep 05 '12 at 06:17
  • 4
    Last time I read "can able", I promised to do something horrible when I encounter it next time. Too bad I don't remember what it was. Apart from that, you don't even need the cast - one can assign any non-function pointer to a `void *`. –  Sep 21 '13 at 05:26
  • Looks valid. [Read this](http://stackoverflow.com/a/12275452/2089675) – smac89 Sep 21 '13 at 05:26
  • 1
    if it compile, it's valid and IMO it must compile. But I dislike those mix of C and C++... – alexbuisson Sep 21 '13 at 05:29
  • 2
    Please. Please. Please avoid casts. C++ has a certain luxury of having types. It avoids possible errors. Besides India is trying to remove casts so why not you? It is the largest democracy on the planet. – Ed Heal Sep 21 '13 at 05:35
  • This, cast to void*, is very useful under conditions where pointers to two different interfaces have to be checked to know if they are indeed from the same object. Especially when the name of the most derived class implementing those interfaces is not known. – nanda Sep 21 '13 at 07:26
  • @nanda - No - If you do not know the name of the interface then get out of the business of writing software – Ed Heal Sep 21 '13 at 08:18

7 Answers7

19

This is perfectly valid. Here is what standard has to say about it:

§4.10 Pointer conversions

2 An rvalue of type "pointer to cv T," where T is an object type, can be converted to an rvalue of type "pointer to cv void." The result of converting a "pointer to cv T" to a "pointer to cv void" points to the start of the storage location where the object of type T resides, as if the object is a most derived object (1.8) of type T (that is, not a base class subobject).

which means you can convert your pointer to class to a void pointer. And ...

§5.2.9 Static cast

10 An rvalue of type "pointer to cv void" can be explicitly converted to a pointer to object type. A value of type pointer to object converted to "pointer to cv void" and back to the original pointer type will have its original value.

which means you can use static_cast to convert a void pointer back to an original class pointer.

Hope it helps. Good Luck!

dyp
  • 38,334
  • 13
  • 112
  • 177
  • 1
    The first quote says btw that a conversion from an object pointer type to `void*` can be implicit (doesn't require a cast). – dyp Sep 21 '13 at 09:50
  • Which Standard/version are you quoting from? As far as I can see, it's neither C++03 nor C++11. – dyp Sep 21 '13 at 09:56
7

I have not seen casting to void* much in C++. It is a practice in C that is actively avoided in C++.

Casting to void* removes all type safety.

If you use reinterpret_cast or static_cast to cast from a pointer type to void* and back to the same pointer type, you are actually guaranteed by the standard that the result will be well-defined.

The hazard is that you may cast a void* to the wrong type, since you are no longer assured of what the correct type was.

Drew Dormann
  • 59,987
  • 13
  • 123
  • 180
6

In C++ you don't need the static cast to get to void*:

int main()
{
    CFoo* foo = new CFoo;
    void* dt = foo;
    tfunc(dt); // or tfunc(foo);

    return 0;
}

NB: your implementation of tfunc() is quite correct in that it does need the cast.

quamrana
  • 37,849
  • 12
  • 53
  • 71
3

What I wonder is if there are any pitfalls/ issues of doing this?

You need to be absolutely sure while casting the the void* back to the particular type, if you don't, you end up with an Undefined behavior and a potential disaster. Once you use void * you lose type safety.It is difficult to keep track of what type a void * is actually pointing to, there is no way to guarantee or determine that it indeed points to the type to which you are going to typecast it back to.

Is there any way that we could run in to an error or undefined condition when we are casting to void* and back again?

Yes, the scenario mentioned in #1.

How should we resolve such issues if there are any?

Avoid using void * in C++ completely, instead use templates and inheritance.
In C you might absoultely need it in certain situations but try to keep its use to a minimum.
Bottomline,
C/C++ allows you to shoot yourself in foot, it is up to you to do or not do so.

Alok Save
  • 202,538
  • 53
  • 430
  • 533
  • If you can afford a tiny perf hit, `std::any` is a safer alternative: `std::any dt = foo; //< dt holds a CFoo*.` then `foo = std::any_cast(dt);` will throw if `dt` somehow doesn't hold a `CFoo*`, rather than invoking UB when you try to dereference a pointer to the wrong type. – Ben Jun 09 '23 at 13:31
3

The only thing the standard grants is that, given A* pa, (A*)(void*)pA == pA. A a consequence

void* pv = pA;
A* pA2 = (A*)pv;
pA2->anything ...

will be te same as pA->anything ...

Everything else is "not defined", ad -in fact- is somehow implementation dependent.

Based on my experience, here are some known pitfalls:

  • Consider A derived form B, pA and pB to be A* and B*. pB=pA makes pB to point to the base of A. That doesnt mean that pB and pA are the same address. hence pB = (B*)(void*)pA can actually point anywhere else into A (although single inheritance objects are commonly implemented sharing the same origin, so it apparently works fine)
  • The same is viceversa: Assuming pB actually is pointing to an A, pA = (A*)(void*)pB don't necessarily point correctly to the A object. The correct way is pA = static_cast<A*>(pB);
  • If the above points can work with the most of single inheritance implementations, will never work with multiple imheritance for bases other than the first: consider class A: public Z, public B { ... }; if Z is not empty, given an A, the B subcomponent will not have the same A address. (and multiple inheritance in C++ is everywhere an iostream is)
  • Sometimes things depend also on the platform: (char*)(void*)pI (where pI points to an integer) will not be the same as "*pI if *pI in(-128..+127)" (it will be only on little endian machines)

In general don't assume conversion between types works just changing the way an address is interpreted.

Emilio Garavaglia
  • 20,229
  • 2
  • 46
  • 63
1

I know a lot of functions within driver etc. using void pointers to return data to the caller, the schema is mostly the same:

int requestSomeData(int kindOfData, void * buffer, int bufferSize);

This function can take different data types as parameter. What they do is using bufferSize as a parameter to avoid writing to memory places they should not write to. If the bufferSize does not match or is smaller than the data that shall be returned, the function will return an error code instead.

Anyway: Avoid using them or think threefold before writing any code.

Nippey
  • 4,708
  • 36
  • 44
1

Is this valid?

Yes, it is valid as per standard § 5.2.9.7

A prvalue of type “pointer to cv1 void” can be converted to a prvalue of type “pointer to cv2 T,” where T is an object type and cv2 is the same cv-qualification as, or greater cv-qualification than, cv1. The null pointer value is converted to the null pointer value of the destination type. A value of type pointer to object converted to “pointer to cv void” and back, possibly with different cv-qualification, shall have its original value. [ Example:

T* p1 = new T;
const T* p2 = static_cast<const T*>(static_cast<void*>(p1));
bool b = p1 == p2; // b will have the value true.
billz
  • 44,644
  • 9
  • 83
  • 100