Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Properly casting a `void*` to an integer in C++

I'm dealing with some code that uses an external library in which you can pass values to callbacks via a void* value.

Unfortunately, the previous person working on this code decided to just pass integers to these callbacks by casting an integer to a void pointer ((void*)val).

I'm now working on cleaning up this mess, and I'm trying to determine the "proper" way to cast an integer to/from a void*. Unfortunately, fixing the use of the void pointers is somewhat beyond the scope of the rework I'm able to do here.

Right now, I'm doing two casts to convert from/to a void pointer:

static_cast<int>(reinterpret_cast<intptr_t>(void_p))

and

reinterpret_cast<void *>(static_cast<intptr_t>(dat_val))

Since I'm on a 64 bit machine, casting directly ((int)void_p) results in the error:

error: cast from 'void*' to 'int' loses precision [-fpermissive]

The original implementation did work with -fpermissive, but I'm trying to get away from that for maintainability and bug-related issues, so I'm trying to do this "properly", e.g. c++ casts.

Casting directly to an int (static_cast<int>(void_p)) fails (error: invalid static_cast from type 'void*' to type 'int'). My understanding of reinterpret_cast is that it basically just causes the compiler to treat the address of the value in question as the cast-to data-type without actually emitting any machine code, so casting an int directly to a void* would be a bad idea because the void* is larger then the int (4/8 bytes respectively).

I think using intptr_t is the correct intermediate here, since it's guaranteed to be large enough to contain the integral value of the void*, and once I have an integer value I can then truncate it without causing the compiler to complain.

Is this the correct, or even a sane approach given I'm stuck having to push data through a void pointer?

like image 327
Fake Name Avatar asked Jun 10 '15 22:06

Fake Name


2 Answers

I think using intptr_t is the correct intermediate here, since it's guaranteed to be large enough to contain the integral value of the void*, and once I have an integer value I can then truncate it without causing the compiler to complain.

Yes, for the reason you mentioned that's the proper intermediate type. By now, if your implementation doesn't offer it, you probably have more problems than just a missing typedef.

Is this the correct, or even a sane approach given I'm stuck having to push data through a void pointer?

Yes, given the constraints, it's quite sane.
You might consider checking the value fits instead of simply truncating it upon unpacking it from the void* in debug-mode, or even making all further processing of that integer use intptr instead of int to avoid truncation.

You could also consider pushing a pointer to an actual int instead of the int itself though that parameter. Be aware that's less efficient though, and opens you to lifetime issues.

like image 71
Deduplicator Avatar answered Oct 23 '22 13:10

Deduplicator


Based on your question, I am assuming that you call a function in some library, passing it a void*, and at some point later in time, it calls one of your functions, passing it that same void*.

There are basically two possible ways to do this; the first is through explicit casting, as you showed in your current code.

The other, which Deduplicator alluded to, is a little less efficient, but allows you to maintain control of the data, and possibly modify it between when you call the library function, and when it calls your callback function. This could be achieved with code similar to this:

void callbackFunction(void* dataPtr){
    int data = *(int*)dataPtr;
    /* DO SOMETHING WITH data */
    delete dataPtr;
}
void callLibraryFunction(int dataToPass){
    int* ptrToPass = new int(dataToPass);
    libraryFunction(ptrToPass,callbackFunction);
}

Which one you should use depends on what you need to do with the data, and whether the ability to modify the data could be useful in the future.

like image 38
Nick Mertin Avatar answered Oct 23 '22 13:10

Nick Mertin