Home > Software engineering >  Why does this code behave differently on C-stdio function overloads? (vfprintf vs. putchar)
Why does this code behave differently on C-stdio function overloads? (vfprintf vs. putchar)

Time:05-23

I'm trying to define various functions with the same name as C stdio to prevent unwanted usage. I encountered an odd situation where the technique works on some functions, but not others. I cannot explain why A::fn calls the stdio version of vfprintf instead of the function definition in the A namespace.

#include <stdio.h>
#include <stdarg.h>

namespace A {

    template <typename... Ts>
    void putchar(int ch) {
        static_assert(sizeof...(Ts) == -1);
    }
    
    template <typename... Ts>
    void vfprintf(FILE* file, const char* fmt, va_list vlist) {
        static_assert(sizeof...(Ts) == -1);
    }

    void fn(const char* fmt, ...)
    {
        putchar('A'); // fails to compile (as expected)
        va_list vlist;
        va_start(vlist, fmt);
        vfprintf(stdout, "Hello!\n", vlist); // does not fail (not expected)
        va_end(vlist);
    }
}

int main()
{
    A::fn("hello");
    return 0;
}

P.S. Happy to hear comments indicating that there is a better way to restrict C-style I/O (maybe clang-tidy).

CodePudding user response:

Replacing standard library routines is UB (citation to follow). See examples here and here for the kind of trouble this can cause.

Edit: OK, here's the promised citation:

The C standard library reserves the following kinds of names:
...
names with external linkage
...
If a program declares or defines a name in a context where it is reserved, other than as explicitly allowed by [library], its behavior is undefined.

But, as discussed in the comments, I'm not sure whether you're doing this in a permitted context or not (although, on reflection, I don't think you are), so I'm going to change tack.

There is, in fact, a very simple way to do what you want. You can do this with #pragma GCC poison. So, taking your example, all you need is:

#pragma GCC poison putchar vfprintf

and you're done. clang also supports this pragma.

Live demo.

Hats, rabbits, we can do it all :) (on good days)

  • Related