liv2hak
liv2hak

Reputation: 14990

macro to check if the return value of a function is being checked

I have a function

void *custom_get_value(ObjectPtr) 

This function traditionally never used to return NULL.It can return any of the following values

uint32_t
int32_t 
uint64_t 
int64_t
uint8_t

Since the function never used to return NULL I have a lot of code that does

       *(uint32_t*)custom_get_value(ObjectPtr)

                    OR

      *(uint64_t*)custom_get_value(ObjectPtr) 

Recently we have decided to modify the behaviour of

void *custom_get_value(ObjectPtr) in such a way that it can return NULL.So all occourances of the above scenario (de-referencing to specific types without checking the return value) can result in segmentation fault.

Can I use some macro to idendify all the places in the code where the return value of

void *custom_get_value(ObjectPtr)

is not being checked.If yes how do I do that?

Upvotes: 2

Views: 779

Answers (2)

M.M
M.M

Reputation: 141618

Name the new function to custom_get_value_ex and delete the old one. Then your compiler will very kindly indicate all uses of the old function, so you can review them.

Generally it is a good idea to review all calls to a function anyway, when you change the semantics of a function that is already in use.

Perhaps you could wrap some of these calls in a more maintainable wrapper, e.g.

inline uint32_t custom_get_uint32(.....) 
{  
     void *value = custom_get_value(.....);
     if ( !value )
          return 0;   // or some other error handling
     return *(uint32_t *)value;
}

Then if you change custom_get_value again , you would only have to fox this wrapper.

Even tidier would be to have a function for getting the item you are interested in:

inline uint32_t get_propeller_setting(.....)
{
     void *value = custom_get_value(PROPELLER_SETTING,........)
     // ....
}

Then you can have specific handling for when each value gets a null pointer returned.

Upvotes: 0

Alex Celeste
Alex Celeste

Reputation: 13370

You can't easily write a macro that can track what happens to a value after it's been returned, because data flow analysis is somewhat beyond the preprocessor's abilities. However, there is a useful technique designed to help with situations such as this, which is to define the function you want to check as a self-referential macro:

#define custom_get_value(...) (0, custom_get_value(__VA_ARGS__))

This example doesn't do anything by itself, but it showcases something useful: when a macro name appears as part of its own replacement list, the nested occurrence of the name is "painted blue" and will not expand a second time. So by defining an existing function name as a macro, you can "wrap" (or "advise") all existing invocations of the function - presumably scattered across your code in an unknown number of places - with extra actions, in addition to retaining the original invocation.

Then you have several options, e.g.:

#define custom_get_value(...) ({ \
    _Pragma("message \"custom_get_value called\""); \
    custom_get_value(__VA_ARGS__); \
})

...if you're on GCC or Clang, should list the file and line number of every point where custom_get_value is called, at compile-time. (Both #pragma message and the statement expression form are nonstandard GCC extensions - but if you're only going to use this while cleaning up old calls, perhaps that doesn't matter? Once all the calls are checked you can remove the nonstandard code.)

Or apply a runtime message instead:

#define custom_get_value(...) \
    custom_wrap(custom_get_value(__VA_ARGS__), __FILE__, __LINE__)

void * custom_wrap(void * val, char * f, int l) {
    printf("custom_get_value called in %s on line %d\n", f, l); return val;
}

...which will drive you nuts printing a message every time the function gets called, but is at least standard.

You could even wrap all the calls with a function that renders the resulting pointer guaranteed to be non-null once again, although that's probably not the best way to deal with this situation.

Upvotes: 3

Related Questions