编译函数是否可用的不同代码

Cet*_*ert 5 c macros conditional-compilation c-preprocessor

Windows仅向Windows Vista提供GetTickCount,并从该操作系统开始提供GetTickCount64.如何通过调用不同的函数来编译C程序?

如何让C编译器检查是否在包含的头文件中声明了一个函数,并根据该特定函数是否可用来编译代码的不同部分?

#if ??????????????????????????????
unsigned long long get_tick_count(void) { return GetTickCount64(); }
#else
unsigned long long get_tick_count(void) { return GetTickCount(); }
#endif
Run Code Online (Sandbox Code Playgroud)

寻找工作样本文件而不仅仅是提示.

编辑:我在(64位)Windows 7 RC上使用minGW中的gcc 3.4.5尝试了以下操作,但它没有帮助.如果这是MinGW问题,我该如何解决这个问题?

#include <windows.h>
#if (WINVER >= 0x0600)
unsigned long long get_tick_count(void) { return 600/*GetTickCount64()*/; }
#else
unsigned long long get_tick_count(void) { return 0/*GetTickCount()*/; }
#endif
Run Code Online (Sandbox Code Playgroud)

RBe*_*eig 13

根据目标Windows版本编译API的时间选择将构建的可执行文件锁定到该版本和更新版本.这是开源,*nix目标项目的常用技术,假定用户将为其平台配置源工具包并编译干净以进行安装.

在Windows上,这不是通常的技术,因为假设最终用户根本没有编译器通常是不安全的,更不用说想要处理构建项目的复杂性了.

通常,只使用所有Windows版本中存在的旧API就足够了.这也很简单:您只需忽略新API的存在.

如果这还不够,您可以使用LoadLibrary()GetProcAddress()尝试在运行时解析新符号.如果无法解决,那么您将回退到较旧的API.

这是一个可能的实现.它会检测第一个调用,并尝试加载库并解析名称"GetTickCount64".在所有调用中,如果指向已解析符号的指针为非null,则调用它并返回结果.否则,它会回退到较旧的API,转换其返回值以匹配包装器的类型.

unsigned long long get_tick_count(void) {
    static int first = 1;
    static ULONGLONG WINAPI (*pGetTickCount64)(void);

    if (first) {
        HMODULE hlib = LoadLibraryA("KERNEL32.DLL");
        pGetTickCount64 = GetProcAddressA(hlib, "GetTickCount64");
        first = 0;
    }
    if (pGetTickCount64)
        return pGetTickCount64();
    return (unsigned long long)GetTickCount();
}
Run Code Online (Sandbox Code Playgroud)

请注意,我使用了... API函数的一种风格,因为已知库名称和符号名称只是ASCII ...如果使用此技术从可能位于文件夹中的已安装DLL加载符号以非ASCII字符命名,那么您将需要担心使用Unicode构建.

这是未经测试的,您的里程会有所不同等等......


gol*_*udo 1

之前的答案已经指出检查针对您的特定情况出现的特定#define。这个答案适用于编译不同代码的更一般情况,无论函数是否可用。

与尝试在 C 文件本身中完成所有操作不同,这才是配置脚本真正发挥作用的地方。如果您在 Linux 上运行,我会毫不犹豫地向您推荐GNU Autotools 。我知道有适用于 Windows 的端口,至少如果您使用 Cygwin 或 MSYS,但我不知道它们有多有效。

一个简单的(而且非常丑陋的)脚本,如果你有 sh 就可以工作(我没有方便的 Windows 设置来测试它),它看起来像这样:

#!/bin/sh

# First, create a .c file that tests for the existance of GetTickCount64()

cat >conftest.c <<_CONFEOF
#include <windows.h>
int main() {
    GetTickCount64();
    return 0;
}
_CONFEOF

# Then, try to actually compile the above .c file

gcc conftest.c -o conftest.out

# Check gcc's return value to determine if it worked.
#   If it returns 0, compilation worked so set CONF_HASGETTICKCOUNT64
#   If it doesn't return 0, there was an error, so probably no GetTickCount64()

if [ $? -eq 0 ]
then
    confdefs='-D CONF_HASGETTICKCOUNT64=1'
fi

# Now get rid of the temporary files we made.

rm conftest.c
rm conftest.out

# And compile your real program, passing CONF_HASGETTICKCOUNT64 if it exists.

gcc $confdefs yourfile.c
Run Code Online (Sandbox Code Playgroud)

这应该很容易翻译成您选择的脚本语言。如果您的程序需要额外的包含路径、编译器标志或其他内容,请确保将必要的标志添加到测试编译和实际编译中。

“yourfile.c”看起来像这样:

#include <windows.h>

unsigned long long get_tick_count(void) {
#ifdef CONF_HASGETTICKCOUNT64
  return GetTickCount64();
#else
  return GetTickCount();
#endif
}
Run Code Online (Sandbox Code Playgroud)