Use a void* argument to pass integer OR a pointer

Viewed 172

I want to detect if a value of a void* type input function argument is an integer or a pointer. Currently, I'm passing the integer only (this is Midnight Commander source):

mc_event_raise (MCEVENT_GROUP_CORE, 
    "clipboard_file_from_ext_clip",
        (void*)(intptr_t)clip_id);

However I would also like to sometimes pass a string (char *) in the same parameter, as extending the API would be difficult. I know that this is wrong, however, maybe there is a fully legal way to obtain such effect ? My ideas: – the addresses are in general aligned to 4 or 8 bytes, so I've could skip such values from the clip_id (jump over them) and then decode the value? if (data % 8) then clip_id = decode_clip_id(data); ? – maybe pointers cannot be as small as 10…20 and a simple check would be sufficient? – other ideas…?

2 Answers

A problem like this can be solved with a structure containing a union and a tag.

struct EventData {
    enum { EV_IS_INT, EV_IS_PTR } type;
    union {
        intptr_t v_int;
        void * v_ptr;
    };
};

So, you can then pass a pointer to this EventData, and the callback can decode whether to access v_int or v_ptr, based on the value of type.

    struct EventData *ed = makeEventData (EV_IS_INT, clip_id);
    mc_event_raise (MCEVENT_GROUP_CORE, 
                    "clipboard_file_from_ext_clip",
                    ed);

If the event is completed asynchronously, you may need to create the event dynamically, and then the callback function would have to release the memory when the callback is done with the argument.

Windows does a lot of this hackery with their HANDLEs and resource IDs.

Your integer needs to have a defined valid range. Anything outside of that range is probably a pointer.

Almost all operating systems reserve low memory areas. You can probably assume that the 16-bit range 0 to 65535 is not a pointer. Beyond that gets iffy.

If your code needs to be portable to things like embedded RTOS or freestanding environments then you cannot assume anything about pointers.

In any case you will probably want some sort of function to create values to pass into your void* which will have an assert or abort to avoid accidentally creating invalid values.

Related