开发者

Is it wrong / dangerous to typecast enum to void*?

开发者 https://www.devze.com 2022-12-21 17:17 出处:网络
The whole code is written in ANSI C, and it should remain so. I have a callback defined like so: typedef enum {

The whole code is written in ANSI C, and it should remain so. I have a callback defined like so:

typedef enum {
    Event_One,
    Event_Two,
    Event_State
} EventEnum;

typedef void (*callback)(EventEnum event, void* data);

The callback recipient interprets data depending on the event value. This is the contract between the components. Sometimes it is a pointer to the structure, sometimes it might be a string, other cases might be other data. I am defining an additional event and setting up a new "contract" that data is an enumeration. Like so:

typedef enum {
    State_Initial = 0,
    State_Running,
    State_Final
} StateEnum;

Then somewhere in the code I have a callback function, which is doing this

void ProcessEvent (EventEnum event, void* data)
{
    if (event == Event_State)
    {
         StateEnum state = (StateEnum)data; /* <<<<<<<<<<< */
         switch (state) {
         case State_Initial:
             <...>
             break;
         case State_Running:
             <...>
             break;
         case State_Final:
             <...>
             break;
         }
    }
}

The callback above is called like so:

{
    callback infoCallback = ProcessEvent开发者_如何转开发; /* This is only for example,
                                             done during initialization */
    <...>
    StateEnum someState = State_Running;
    <...>
    infoCallback(Event_State, (void*)someState); /* <<<<<<<<<<<<<<<<<<< */
}

Is there anything fundamentally wrong with typecasting void* to StateEnum and vice versa? What are the possible gotchas in this way? Any thoughts on testability and maintainability?

EDIT : The code compiles, links and runs OK right now. I want to know why this should not be done and if there are any real reasons why the code must be changed.


Only pointers to objects (i.e., not functions) can be converted to void * and back. You can't convert a non-pointer to void * and back. So, change your call to:

infoCallback(Event_State, &someState);

And your function to:

StateEnum *state = data;
switch (*state)
...

From the standard (6.3.2.3):

An integer may be converted to any pointer type. Except as previously specified, the result is implementation-defined, might not be correctly aligned, might not point to an entity of the referenced type, and might be a trap representation.

Any pointer type may be converted to an integer type. Except as previously specified, the result is implementation-defined. If the result cannot be represented in the integer type, the behavior is undefined. The result need not be in the range of values of any integer type.

So, what you are doing is implementation-defined. If your implementation defines it to be OK to convert int to a pointer and back, then the code will work. In general, it is not portable. For more details, see this thread on comp.lang.c.

C99 additionally defines types intptr_t and uintptr_t, which are integral types, and it is guaranteed to convert a void * to them and back.


What you are doing is implementation defined. There is no guarantee that the enum will have an identical value after being converted to a point-to-void and then converted back. If you really need to be sure, you should use intptr_t or uintptr_t instead of an enum. They are guaranteed to have the same value after being cast to a pointer-to-void and back again.


Alok is absolutely correct that conversions between integers and pointers are implementation-defined, and hence entirely non-portable. It would seem to be completely allowable, for example, if all casts of void * values to integral values always produced 0 (on an implementation that does not provide intptr_t or uintptr_t, anyway).

A common idiom is to extend the contract such that the callback will receive a pointer to a dynamically allocated enum, which it must free. In the caller:

StateEnum *someState = malloc(sizeof *someState);
*someState = State_Running;

infoCallback(Event_State, someState);

...and in the callback:

void ProcessEvent (EventEnum event, void* data)
{
    if (event == Event_State)
    {
         StateEnum state = *(StateEnum *)data;
         free(data);
         switch (state) {


Although the compiler allows this, and in some cases it may be ok you need to be really careful about the lifetime of the objects.

The main problem I can see is that unless the processEvent function occurs in the same call chain as infoCallback, the original someState variable may go out of scope. So later when you refer to is in infoCallback, the void* will be undefined.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号