Michael
Michael

Reputation: 4770

Check whether function is declared with C preprocessor?

Is it possible to tell the C preprocessor to check whether a function (not a macro) is declared? I tried the following, but it doesn't appear to work:

#include <stdio.h>

int main(void)
{
#if defined(printf)
    printf("You support printf!\n");
#else
    puts("Either you don't support printf, or this test doesn't work.");
#endif
    return 0;
}

Upvotes: 22

Views: 16470

Answers (6)

Petr Skocik
Petr Skocik

Reputation: 60068

The traditional solution has been to use a config system to define macros.

But you can detect the existence or nonexistence of a prototype from C too and turn it into a compile-time integer constant expression (suitable for _Static_asserts, constant initializers, easily eliminated branches, etc., though not preprocessor #if conditions).

You need

  • C11 _Generic (or before that nonstandardly __builtin_choose_expr)
  • C23 turned off so you int foo(); is still a prototypeless declaration and not equivalent to int foo(void);

Then you can use the properties of how prototypeless declaration combine with prototypes in type compatibility checks. On clang you might want to locally suppress warnings around deprecated-non-prototypes.

Here's an example:

#define _GNU_SOURCE
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>


#if __clang__
    #define begin_ignoreWdeprecatedNonPrototype$() _Pragma("GCC diagnostic push")  _Pragma("GCC diagnostic ignored \"-Wdeprecated-non-prototype\"") 
    #define close_ignoreWdeprecatedNonPrototype$() _Pragma("GCC diagnostic pop")
#else
    #define begin_ignoreWdeprecatedNonPrototype$()
    #define close_ignoreWdeprecatedNonPrototype$()
#endif
typedef struct incomplete_type incomplete_type;
#define ifPrototypedToRetTpThenOrElse$(Tp_t,FuncName,ThenDo,OtherwiseDo) \
    do{  \
        begin_ignoreWdeprecatedNonPrototype$() \
        Tp_t FuncName(); \
        if(_Generic(&FuncName, default:1,Tp_t(*)(incomplete_type):0)){ThenDo;} \
        else{OtherwiseDo;};\
        close_ignoreWdeprecatedNonPrototype$() \
    }while(0)


int main(void){
    ifPrototypedToRetTpThenOrElse$(int,foo, puts("foo ✓");, puts("foo x"));
    ifPrototypedToRetTpThenOrElse$(int,puts, puts("puts ✓");, puts("puts x"));
    ifPrototypedToRetTpThenOrElse$(int,execveat, execveat(AT_FDCWD,"/usr/bin/echo",(char*[]){"echo","execveat ✓",0},0,0) ;, puts("execveat ×"));
}

The above yields:

foo x
puts ✓
execveat ×

on Linux if _GNU_SOURCE isn't defined before the unistd.h inclusion and

foo x
puts ✓
execveat ✓

if it is (the macro causes unistd.h to expose that prototype).

Tested and confirmed to work on clang, gcc and tinycc.

Upvotes: 2

Sidepipe
Sidepipe

Reputation: 139

Strictly speaking no, the preprocessor can't do it on its own. However, you can give it a little help by creating appropriate #defines automatically.

Normally as was mentioned above, you'd use autotools if on a unix type system. However, you can also create the same effect using a makefile. I recently had cause to detect the "posix_fallocate" function being defined in headers, because I was using uClibc which seemed to omit it in earlier versions. This works in gnu make, but you can probably get a similar thing to work in other versions:

NOFALLOC := $(shell echo "\#include <fcntl.h>\nint main() { posix_fallocate(0,0,0);}" | $(CC) -o /dev/null -Werror -xc - >/dev/null 2>/dev/null && echo 0 || echo 1)
ifeq "$(NOFALLOC)" "1"
    DFLAGS += -DNO_POSIX_FALLOCATE
endif

Upvotes: 3

James Black
James Black

Reputation: 41858

If you look at tools like autoconf you will see that they go through many tests to determine what a computer has or doesn't have, to compile properly, then they set the correct #DEFINES.

You may want to look at that model, and that tool if you are on some flavor of unix, as what you want to do isn't going to be possible, as others undoubtedly are pointing out.

Upvotes: 6

dreamlax
dreamlax

Reputation: 95335

The preprocessor is a simple program and knows next to nothing about the underlying language. It cannot tell if a function has been declared. Even if it could, the function may be defined in another library and the symbol is resolved during linking, so the preprocessor could not help in that regard.

Upvotes: 2

Bj&#246;rn Pollex
Bj&#246;rn Pollex

Reputation: 76788

Since the preprocessor is not aware of the language C/C++ (it really only does text-replacement) I would guess that this is not possible. Why do you want to do this? Maybe there is another way.

Upvotes: 0

laalto
laalto

Reputation: 152817

No. Preprocessor runs before the C compiler and the C compiler processes function declarations. The preprocessor is only there for text processing.

However, most header files have include guard macros like _STDIO_H_ that you can test for in the preprocessor stage. However, that solution is not portable as the include guard macro names are not standardized.

Upvotes: 27

Related Questions