5

I found this question very interesting: How to force compilation error if function return value is not checked?

It's about enforcing compilation errors if you do not check the return value. I wrote an answer to that question where you can use gcc extensions like this:

__attribute__ ((warn_unused_result)) int foo (void) 
{
    return 5;
}

to enforce a warning and the compile with -Werror=unused-result to make the compiler generate an error if you don't use the return value somehow.

Now I would like to create wrapper functions to the regular standard functions. An idea is to rename them like this:

__attribute__ ((warn_unused_result)) realloc_wrapper(void *ptr, size_t new_size)
{
    return realloc(ptr, new_size);
}

But the problem is that this forces me to use a different name, which would cause a lot of search and replace. Granted, this can be done automatically, but still. Preferably, I would like to be able to create a header that I can use instead of a standard C header for any program. One use case is when debugging a big program. Then this would instantly point me to potential causes of bugs.

TL;DR

So in short, I want to be able to take this program:

#include <stdlib.h>

int main(void)
{
    char *ptr;
    realloc(ptr, 42);
}

and change it to:

// Replaced stdlib with a custom header
#include <mystdlib.h>

int main(void)
{
    char *ptr;
    realloc(ptr, 42);
}

and then the line with realloc should generate a warning.

I might add that I'm ok with a solution that isn't 100% perfect. The intended use is for debugging and not production code.

EDIT:

I just noticed that realloc was a bad choice, since it seems to already have this declaration by default, but I used PSkocik and made it work for fgets.

klutt
  • 30,332
  • 17
  • 55
  • 95

1 Answers1

5

A straightforward solution would be to shadow the function with an identically named macro. (I'll use puts as an example, because, as you've mentioned, realloc is already usually declared with warn_unused_result)

/*begin your header:*/

#include <stdio.h>

__attribute ((__warn_unused_result__)) static inline
int puts_wrapper(char const*X) 
{ 
   return (puts)(X); 
}
#define puts(X) puts_wrapper(X)

/*end your header*/

int main(void) { puts("hello, world"); }

(The parentheses around puts aren't necessary but they allow you to move the define before the puts_wrapper definition if you wanted to.)

Alternatively, you could simply redeclare the function with the warn_unused_result attribute added (works on both gcc and clang).

/*begin your header*/
#include <stdio.h>
__attribute ((__warn_unused_result__)) int puts(char const*);
/*end your header*/ 

int main(void) { puts("hello, world"); }
Petr Skocik
  • 58,047
  • 6
  • 95
  • 142
  • If OP `#include`'s this header, will that not also be including `stdlib.h`? thereby causing conflicts? (i.e., both the `#define realloc` and the `stdlib.h` `realloc` would be visible. – ryyker Oct 15 '19 at 13:50
  • 2
    @ryyker No, because the symbol `realloc` will still have its meaning from the system header up until the point the `#define` is encountered. Also, because this file does `#include ` first, any subsequent inclusion of this file will be a no-op due to the include guards in the file. – dbush Oct 15 '19 at 13:55
  • I realized that this is not needed for the particular function `realloc` but it worked fine for `fgets`. – klutt Oct 15 '19 at 13:57
  • @ryyker What conflicts? The macro will oveshadow the external definition. If the macro were defined *before* including `stdlib.h`, now that could cause problems if `stdlib` defines `realloc` as `void *realloc(void *, size_t);` rather than `void *(realloc)(void *, size_t)` (and it probably does), but if you include before the define, there shouldn't be a problem. Admittedly, some standard library identifiers are allowed to be macros and now those would be problematic to redefine. Personally, I prefer wrappers with distinct names, but since the OP didn't want that ... – Petr Skocik Oct 15 '19 at 13:58
  • @PSkocik - Yes. dbush's comment cleared that up for me. Thanks. And btw, I have up-clicked your answer. Unless you object, I will leave the comment because I think other's will have the same question. (let me know if you feel otherwise, and I will remove it. :) – ryyker Oct 15 '19 at 14:22
  • 1
    It could be an idea to include the comments in the answer to clarify this. – klutt Oct 15 '19 at 14:24
  • I think you kept a bit of the junk from the original post in your first example; `puts` does not take a `size` argument (or a `void` pointer argument either, for that matter). – S.S. Anne Oct 16 '19 at 11:27
  • @JL2210 Indeed. Thanks. – Petr Skocik Oct 16 '19 at 11:32