The first thing that sticks out as problematic is the assertion that a level of indirection around tmpfile is going to be slow. There are two ways we can determine if this is true; Try It And See, or read the output of the compiler to see what code it generates. Let's try the second one.
We'll make a file called slow.c with the contents:
int f(void){
return 42;
}
int g(void){
return f();
}
Then we can see what code gcc generates with the -S option: $ gcc -S slow.c
and looking at slow.s. Here's the important part of g (with some annotations removed): g:
.LFB1:
pushq %rbp
movq %rsp, %rbp
call f
popq %rbp
ret
So, we're calling f here. But you don't compile your production code with -O0, so let's try -O3. Here's f: f:
.LFB0:
movl $42, %eax
ret
And here's g: g:
.LFB1:
movl $42, %eax
ret
Notice how we're not calling f anymore; we just get f's body right in our function. So there's no speed cost to writing a function that only calls another function. This is the simplest possible case for an optimizer to optimize, and of course it does.If you want to be explicit, you can always inline g. Then, at g's call site, we see:
main:
.LFB2:
pushq %rbp
movq %rsp, %rbp
call f
movl $0, %eax
popq %rbp
ret
Which is almost the same thing as what -O3 produced, except g's body is copied into its caller instead of f's body being copied into g. (And this is at -O0.)Another case where the thought process goes awry is the guess that there is a preprocessor bug. It's easy to verify, run gcc with -E and see what you get. Here's our test case:
#define foo f
int f(void){
return 42;
}
int g(void){
return foo();
}
Preprocess: $ gcc -E buggy.c
int f(void){
return 42;
}
int g(void){
return f();
}
And look. No bug!So let's move on to the final problem, the weird comment. We'll again see what our preprocessor output looks like on a minimal test case:
#define FILE int
#ifdef _WIN32
# define tmpfile w32_tmpfile
#endif
FILE *tmpfile(void) { return (FILE *) 0 }
FILE *w32_tmpfile ( void ) { return (FILE *) 0 }
void test(void){
FILE *g;
if (code == 200) { // Downloading whole file
/* Write new file (plus allow reading once we finish) */
// FIXME Win32 native version fails here because Microsoft's version of tmpfile() creates the file in C:\
g = fname ? fopen(fname, "w+") : tmpfile();
}
}
We end up with: int *tmpfile(void) { return (int *) 0 }
int *w32_tmpfile ( void ) { return (int *) 0 }
void test(void){
int *g;
if (code == 200) {
}
}
Which looks like our code has suspiciously gone missing. Let's try
removing the comments: int *tmpfile(void) { return (int *) 0 }
int *w32_tmpfile ( void ) { return (int *) 0 }
void test(void){
int *g;
if (code == 200) {
g = fname ? fopen(fname, "w+") : tmpfile();
}
}
And hey, our code is back. Must be something to do with the comments.Anyway, the C toolchain is very simple, so let the computer do the compilation for you when you suspect the compiler isn't doing what you want. Read and understand its output; don't rely on a tool that does something that you couldn't do yourself.
Something I mentioned in that code completion article the other day was "don't rely on your editor to be your brain". This got a lot of downvotes, but this is exactly the problem the author has here. His editor didn't interpret the source code in the same way that the compiler did, and that's what matters.
Learn the language you are using so that you can spot bugs! Don't think something isn't a comment just because your editor doesn't show it in italic orange! Suspect an editor bug, not a compiler bug.
And finally, if you use correct grammar in your comments, this wouldn't have happened:
/* We use our own tmpfile implementation here, since Windows' writes directly to C:/. */