簡體   English   中英

編譯函數是否可用的不同代碼

[英]Compile different code on whether a function is available or not

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

尋找工作樣本文件而不僅僅是提示。

編輯:我在(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

根據目標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();
}

請注意,我使用了... API函數的一種風格,因為已知庫名稱和符號名稱只是ASCII ...如果使用此技術從可能位於文件夾中的已安裝DLL加載符號以非ASCII字符命名,那么您將需要擔心使用Unicode構建。

這是未經測試的,您的里程會有所不同等等......

您可以使用Windows標頭中的預處理器定義來實現它。

unsigned long long
get_tick_count(void)
{
#if WINVER >= 0x0600
    return GetTickCount64();
#else
    return GetTickCount();
#endif
}

處理這類問題的正確方法是檢查函數是否可用,但在項目編譯期間無法可靠地完成。 您應該添加一個配置階段,其中的詳細信息取決於您的構建工具,cmake和scons,兩個跨平台構建工具,提供設施。 基本上,它是這樣的:

/* config.h */
#define HAVE_GETTICKSCOUNT64_FUNC

然后在您的項目中,您執行以下操作:

#include "config.h"

#ifdef HAVE_GETTICKSCOUNT64_FUNC
....
#else
...
#endif

雖然它看起來與顯而易見的方式相似,但從長遠來看它更易於維護。 特別是,您應該盡可能地避免依賴於版本,並檢查功能。 快速檢查版本會導致復雜的交錯條件,而使用上述技術,一切都由一個config.h控制,希望自動生成。

在scons和cmake中,它們將具有自動運行以檢查函數是否可用的測試,並根據檢查在config.h中定義變量。 基本思想是將能力檢測/設置與代碼分離

請注意,這可以處理需要構建在不同平台上運行的二進制文件的情況(比如在XP上運行即使構建在Vista上)。 這只是改變config.h的問題。 如果dones poperly,那只需要更改config.h(你可以在任何平台上生成一個生成config.h的腳本,然后為windows xp,Vista等收集config.h)。 我認為它根本不是針對unix的。

天兒真好,

不是NTDDI_VERSION你需要尋找什么?

更新:您想檢查WINVER是否為0x0600。 如果是,那么你正在運行Vista。

編輯:對於語義啄木鳥頭,我的意思是在Vista環境中運行編譯器。 問題僅涉及編譯,問題僅涉及僅在編譯時使用的頭文件。 大多數人都明白,你打算在Vista環境中進行編譯。 該問題沒有提到運行時行為。

除非有人正在運行Vista,並且可能為Windows XP編譯?

嘖!

HTH

干杯,

你問的是C,但這個問題也被標記為C ++ ......

在C ++中你會使用SFINAE技術,看到類似的問題:

是否可以編寫模板來檢查函數的存在?

但是在提供時在Windows中使用預處理程序指令。

以前的答案已指出檢查特定情況下的特定#define。 這個答案是針對編譯不同代碼的更一般情況,無論函數是否可用。

而不是試圖在C文件本身中做所有事情,這是配置腳本真正發揮作用的那種東西。 如果你在linux上運行,我會毫不猶豫地指向你的GNU Autotools 我知道有可用於Windows的端口,至少如果你使用Cygwin或MSYS,但我不知道它們有多有效。

一個簡單(非常非常丑陋)的腳本如果你有方便的話可以工作(我沒有方便的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

這應該很容易轉換為您選擇的腳本語言。 如果您的程序需要額外的包含路徑,編譯器標志或其他,請確保在測試編譯和實際編譯中添加必要的標志。

'yourfile.c'看起來像這樣:

#include <windows.h>

unsigned long long get_tick_count(void) {
#ifdef CONF_HASGETTICKCOUNT64
  return GetTickCount64();
#else
  return GetTickCount();
#endif
}

如果您的代碼將在Vista之前的操作系統上運行,那么您不能將調用編譯為GetTickCount64(),因為在XP機器上不存在GetTickCount64()。

您需要在運行時確定您正在運行的操作系統,然后調用正確的函數。 通常,兩個調用都需要在代碼中。

如果你真的不需要在Vista機器上調用GetTickCount64()和在XP機器上調用GetTickCount(),那么現在情況可能並非如此。 無論您運行的操作系統是什么,您都可以調用GetTickCount()。 在文檔中沒有跡象表明我們已經看到他們正在從API中刪除GetTickCount()。

我還要指出,GetTickCount()可能根本不適合使用。 文檔說它返回了幾毫秒,但實際上函數的精度甚至不接近1毫秒。 取決於機器(並且在運行時AFAIK無法知道),精度可能是40毫秒甚至更高。 如果您需要1毫秒的精度,您應該使用QueryPerformanceCounter() 實際上,在你使用GetTickCount()的所有情況下都沒有使用QPC的實際理由。

編譯64位計算機時,Microsoft編譯器將定義_WIN64。

http://msdn.microsoft.com/en-us/library/b0084kay%28VS.80%29.aspx

#if defined(_WIN64)
unsigned long long get_tick_count(void) { return GetTickCount64(); }
#else
unsigned long long get_tick_count(void) { return GetTickCount(); }
#endif

如果你必須支持Vista之前,我會堅持只使用GetTickCount()。 否則,您必須實現運行時代碼以檢查Windows版本,並在Vista及更高版本的Vista之前版本和GetTickCount64()上調用GetTickCount()。 由於它們返回不同大小的值(ULONGLONG v DWORD),因此您還需要單獨處理它們返回的內容。 僅使用GetTickCount()(並檢查溢出)將適用於這兩種情況,而在可用時使用GetTickCount64()會增加代碼復雜性並使您必須編寫的代碼量翻倍。

堅持只使用GetTickCount(),直到您可以確定您的應用程序不再需要在Vista之前的計算機上運行。

也許它是GetTickCount()的一個很好的替代品

double __stdcall
thetimer (int value)
{
    static double freq = 0;
    static LARGE_INTEGER first;
    static LARGE_INTEGER second;

    if (0 == value)
        {
            if (freq == 0)
            {
                QueryPerformanceFrequency (&first);
                freq = (double) first.QuadPart;
            }
            QueryPerformanceCounter (&first);
            return 0;
        }
    if (1 == value)
        {
            QueryPerformanceCounter (&second);
            second.QuadPart = second.QuadPart - first.QuadPart;
            return (double) second.QuadPart / freq;
        }
    return 0;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM