12

A codebase I work with has historically tried--purposefully--to avoid dependencies on stdio.h creeping in. It has its own print formatting and mechanisms, and those are what's supposed to be used instead of printf etc.

But someone adds a dependency every so often that has to get noticed and taken out. So I tried to make an alarm for the easiest of cases:

#if !defined(NDEBUG)
   void printf(float dont_link_with_stdio_h);
#endif

The gcc people seem to have been thinking along the lines of stopping easy errors too, because there's a helpful message if you do this...whether you've included <stdio.h> or not.

conflicting types for built-in function 'printf'

There's a way to turn this warning off (-fno-builtin). And there are all kinds of approaches that would do things like filter the symbol dump for things you don't want to be there...

But is there a trivially easy non-warning-causing (if you didn't include stdio.h) way to alert someone that they've introduced an unwanted printf usage?

  • Can you not remove the compiled version of the library (.lib) so that it won't compile/link? – Lee Taylor Dec 30 '15 at 03:24
  • @LeeTaylor I'd prefer to not tamper with the compilation environment, which is used for other things which include this library (and those things are free to include stdio.h if they wish). I'm looking for something less intrusive than using the method I was trying and adding `-fno-builtin`...e.g. something that can be done inside the source alone. – HostileFork says dont trust SE Dec 30 '15 at 03:27
  • 2
    Redefine types/structs rather than functions. – n. m. could be an AI Dec 30 '15 at 03:27
  • Could you `#define printf dont_include_stdio_h` (or `#define printf _Static_assert(0, "Do not include stdio.h")`)? – Cornstalks Dec 30 '15 at 03:35
  • @Cornstalks I didn't think of #define because is that I feared it wouldn't give a noticeable message...but by making it into junk as `#define printf $dont_include_stdio_h$ it properly messes things up in a way that is a good enough alarm. I'd take that as an answer... – HostileFork says dont trust SE Dec 30 '15 at 04:30

3 Answers3

6

You can redefine printf to be some nasty value that will cause a compilation or linking error. For example:

#define printf do_not_include_stdio_h
#include <stdio.h>

int main(void) {
    printf("Hello, world!\n");
    return 0;
}

produces the output:

undefined reference to `do_not_include_stdio_h'

You can munge the macro if you want it to be an even more obscure name or include invalid symbols if you're worried that some poor soul will have defined do_not_include_stdio_h.

You can set the macro definition in the compiler flags so you don't have to manually edit the file(s). For example:

gcc -Dprintf=do_not_include_stdio_h my_file.c
Cornstalks
  • 37,137
  • 18
  • 79
  • 144
3

I wouldn't touch the source files at all. I'd modify the build script. Much easier to maintain, and much easier to prevent people from circumventing the restriction (e.g. by changing the code which causes compilation to fail).

For example, in a makefile, assuming you have an all target that build everything

all:

   grep stdio *.h *.c
   if ["$?" -eq 0 ]; then
       echo "Do not use stdio.  Contact Joe for info"; exit 2;
   fi

   <other stuff to do the build here>

You can also do it on particular targets. For example, if you have a target that compiles a .c file to produce a .o file, just check the .c file before compiling it.

  %.o : %.c

       grep stdio $<
       if ["$?" -eq 0 ]; then
           echo "Do not use stdio.  Contact Joe for info"; exit 2;
       fi

       $(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@

Your only problem now is what to do if you have someone who is determined to bypass your restriction (e.g. by #include "bypass.joe" where bypass.joe has an #include <stdio.h>). For that, look up tools to generate dependencies (e.g. gcc -MM, makedepend, etc) and use that to set up a way to search all of the files your source files depend on. If someone is that determined, also set protections on your makefiles so only you can edit them.

EDIT: If you have a tool set up to generate a dependency file, simply search that file for stdio. If any compilation unit, directly or indirectly, includes stdio.h , then it will be listed in the dependency file.

Peter
  • 35,646
  • 4
  • 32
  • 74
  • The problem with grepping like this (beyond being invasive to the makefiles which I'd rather avoid as they are generated by yet another process) is that then you have worries like what if it is in a comment, what if it's #ifdef'd out and not applicable, etc. There are some third-party files that are .c which have their own debug mode flags separate, so there'd have to be exception lists, etc... – HostileFork says dont trust SE Dec 30 '15 at 12:56
  • Given a choice of being "invasive" on a makefile and being "invasive" on all of the compilation units (aka source files) in a project, I would choose the first. Even in a complex project, the number of makefiles rarely exceeds the number of compilation units. There are techniques to ensure custom handling within automatically generated makefiles, using make recursively, etc. – Peter Dec 30 '15 at 21:54
  • Reasonable, and thank you for the option, upvoted. But for my particular case the better choice in the balance is to not touch makefiles... – HostileFork says dont trust SE Dec 31 '15 at 02:11
2

To prevent inclusion of <stdio.h>, I would go with

#if !defined(NDEBUG)
#if defined(EOF)
#error Do not include stdio.h, contact Joe for more information
#endif
#endif
user3386109
  • 34,287
  • 7
  • 49
  • 68
  • 1
    Nice... though the issue with that would be that it would only stop the inclusions if this inclusion were *after* stdio.h. I'd like to preempt it if this header were included before, too... – HostileFork says dont trust SE Dec 30 '15 at 04:35