检查函数是否用 C 预处理器声明?

发布于 2024-08-11 16:27:14 字数 297 浏览 4 评论 0原文

是否可以告诉C预处理器检查是否声明了函数(而不是宏)?我尝试了以下方法,但它似乎不起作用:

#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;
}

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;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

多情出卖 2024-08-18 16:27:14

不会。预处理器在 C 编译器之前运行,C 编译器处理函数声明。预处理器仅用于文本处理。

但是,大多数头文件都包含诸如 _STDIO_H_ 之类的保护宏,您可以在预处理器阶段对其进行测试。但是,该解决方案不可移植,因为包含保护宏名称未标准化。

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.

风吹雪碎 2024-08-18 16:27:14

如果您查看像 autoconf 这样的工具,您会发现它们经过许多测试来确定计算机有或没有什么,正确编译,然后它们设置正确的#DEFINES。

如果您使用的是某种风格的 unix,您可能想要查看该模型和该工具,因为您想做的事情是不可能的,正如其他人无疑指出的那样。

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.

安静被遗忘 2024-08-18 16:27:14

严格来说不,预处理器不能单独完成它。但是,您可以通过自动创建适当的 #define 来提供一些帮助。

通常如上所述,如果在 UNIX 类型系统上,您将使用自动工具。但是,您也可以使用 makefile 创建相同的效果。我最近有原因检测到标头中定义的“posix_fallocate”函数,因为我使用的是 uClibc,它似乎在早期版本中省略了它。这在 gnu make 中有效,但您可能可以在其他版本中得到类似的东西:

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

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
一紙繁鸢 2024-08-18 16:27:14

预处理器是一个简单的程序,对底层语言几乎一无所知。它无法判断函数是否已声明。即使可以,该函数也可能在另一个库中定义,并且该符号在链接期间解析,因此预处理器在这方面无能为力。

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.

ゝ偶尔ゞ 2024-08-18 16:27:14

传统的解决方案是使用配置系统来定义宏。

但是您也可以从 C 检测原型是否存在,并将其转换为编译时整型常量表达式(适用于 _Static_asserts、常量初始值设定项、轻松消除的分支等,但不适用于预处理器) #如果条件)。

您需要

  • C11 _Generic (或在非标准 __builtin_choose_expr 之前)
  • 关闭 C23,这样您 int foo(); 仍然是无原型声明并且不等效to int foo(void);

然后,您可以在类型兼容性检查中使用无原型声明如何与原型结合的属性。在 clang 上,您可能希望在本地抑制有关已弃用的非原型的警告。

下面是一个示例:

#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 ×"));
}

上面的结果是:

foo x
puts ✓
execveat ×

在 Linux 上,如果在 unistd.h 包含之前未定义 _GNU_SOURCE,并且

foo x
puts ✓
execveat ✓

如果已定义(该宏会导致 unistd.h 来公开该原型)。

经测试并确认可在 clang、gcc 和tinycc 上工作。

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.

雨轻弹 2024-08-18 16:27:14

由于预处理器不知道 C/C++ 语言(它实际上只进行文本替换),我猜这是不可能的。你为什么要这样做?也许还有另一种方法。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文