![](/img/trans.png)
[英]whether the environment variable exported in c function will be available in parent shell?
[英]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
干杯,
以前的答案已指出檢查特定情況下的特定#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.