Скомпилируйте другой код того, доступна ли функция или нет

В Python datetime.now() может производить значение с большей точностью, чем time.time():

from datetime import datetime, timezone, timedelta

now = datetime.now(timezone.utc)
epoch = datetime(1970, 1, 1, tzinfo=timezone.utc) # use POSIX epoch
posix_timestamp_micros = (now - epoch) // timedelta(microseconds=1)
posix_timestamp_millis = posix_timestamp_micros // 1000 # or `/ 1e3` for float

Теоретически, time.gmtime(0) (эпоха, используемая time.time()) может отличаться от 1970.

5
задан Brian Tompsett - 汤莱恩 10 February 2016 в 20:13
поделиться

10 ответов

Предыдущие ответы указали на проверку конкретного #define, который будет присутствовать для вашего конкретного случая. Этот ответ относится к более общему случаю компиляции другого кода, независимо от того, доступна функция или нет.

Вместо того, чтобы пытаться делать все в самом файле C, это то, что действительно помогает скриптам configure. Если бы вы работали в 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

Это должно быть достаточно легко для перевода на выбранный вами язык сценариев. Если вашей программе требуются дополнительные пути включения, флаги компилятора или что-то еще, обязательно добавьте необходимые флаги как в тестовую компиляцию, так и в настоящую компиляцию.

'yourfile.c' будет выглядеть примерно так:

#include <windows.h>

unsigned long long get_tick_count(void) {
#ifdef CONF_HASGETTICKCOUNT64
  return GetTickCount64();
#else
  return GetTickCount();
#endif
}
1
ответ дан 18 December 2019 в 06:51
поделиться

Вы можете достичь этого, используя определения препроцессора в заголовках Windows .

unsigned long long
get_tick_count(void)
{
#if WINVER >= 0x0600
    return GetTickCount64();
#else
    return GetTickCount();
#endif
}
4
ответ дан 18 December 2019 в 06:51
поделиться

Правильный способ справиться с подобными проблемами - это проверить, доступна ли функция, но это не может быть надежно сделано во время компиляции проекта. Вы должны добавить этап конфигурации, детали которого зависят от вашего инструмента сборки, и 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. Если все сделано правильно, это просто вопрос изменения config.h (у вас может быть сценарий, который генерирует config.h на любой платформе, а затем собирает config.h для Windows XP, Vista и т. Д.). Я не думаю, что это вообще относится к unix.

2
ответ дан 18 December 2019 в 06:51
поделиться

Вы спрашиваете о C, но вопрос также помечен как C ++ ...

В C ++ вы бы использовали SFINAE см. аналогичные вопросы:

Можно ли написать шаблон для проверки существования функции?

Но используйте директивы препроцессора в Windows, если они есть.

1
ответ дан 18 December 2019 в 06:51
поделиться

Добрый день,

Разве NTDDI_VERSION не то, что вам нужно?

Обновление: Вы хотите проверить, имеет ли WINVER значение 0x0600. Если это так, то вы используете Vista.

Edit: Для семантической головки Pecker я имел в виду запуск компилятора в среде Vista. Вопрос касается только компиляции, вопрос касается только файлов заголовков, которые используются только во время компиляции. Большинство людей понимало, что предполагалось, что вы компилируете в среде Vista. Вопрос не имел отношения к поведению во время выполнения.

Если кто-то не работает под Vista и может компилировать для Windows XP?

Блин!

HTH

ура,

1
ответ дан 18 December 2019 в 06:51
поделиться

Компилятор Microsoft определит _WIN64 при компиляции для 64-битных машин.

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
0
ответ дан 18 December 2019 в 06:51
поделиться

Если ваш код будет работать в операционных системах до Vista, вы не можете просто скомпилировать свои вызовы в GetTickCount64 (), потому что GetTickCount64 () не существует на машине XP.

Вам нужно определить во время выполнения, какую операционную систему вы используете, а затем вызвать правильную функцию. В общем, оба вызова должны быть в коде.

Теперь это может быть неверно в вашем случае, если вам действительно не нужно иметь возможность вызывать GetTickCount64 () на машинах Vista + и GetTickCount () на машинах XP . Вы можете просто вызвать GetTickCount () независимо от того, на какой ОС вы работаете. В документах нет никаких указаний на то, что они удаляют GetTickCount () из API.

Я также хотел бы указать, что, возможно, GetTickCount () вообще не подходит. В документации говорится, что он возвращает количество миллисекунд, но на самом деле точность функции даже близко не к 1 миллисекунде. В зависимости от машины (а во время выполнения AFAIK это невозможно узнать) точность может составлять 40 миллисекунд или даже больше. Если вам нужна точность в 1 миллисекунду, вы должны использовать QueryPerformanceCounter () . Фактически, действительно нет никаких практических причин не использовать QPC во всех случаях, когда вы все равно использовали бы GetTickCount ().

В документах нет никаких указаний на то, что они удаляют GetTickCount () из API.

Я также хотел бы указать, что, возможно, GetTickCount () вообще не подходит. В документации говорится, что он возвращает количество миллисекунд, но на самом деле точность функции даже близко не к 1 миллисекунде. В зависимости от машины (а во время выполнения AFAIK это невозможно узнать) точность может составлять 40 миллисекунд или даже больше. Если вам нужна точность в 1 миллисекунду, вы должны использовать QueryPerformanceCounter () . Фактически, действительно нет никаких практических причин не использовать QPC во всех случаях, когда вы все равно использовали бы GetTickCount ().

В документах нет никаких указаний на то, что они удаляют GetTickCount () из API.

Я также хотел бы указать, что, возможно, GetTickCount () вообще не подходит. В документации говорится, что он возвращает количество миллисекунд, но на самом деле точность функции даже близко не к 1 миллисекунде. В зависимости от машины (а во время выполнения AFAIK это невозможно узнать) точность может составлять 40 миллисекунд или даже больше. Если вам нужна точность в 1 миллисекунду, вы должны использовать QueryPerformanceCounter () . Фактически, действительно нет никаких практических причин не использовать QPC во всех случаях, когда вы все равно использовали бы GetTickCount ().

В документации говорится, что он возвращает количество миллисекунд, но на самом деле точность функции даже близко не к 1 миллисекунде. В зависимости от машины (а во время выполнения AFAIK это невозможно узнать) точность может составлять 40 миллисекунд или даже больше. Если вам нужна точность в 1 миллисекунду, вы должны использовать QueryPerformanceCounter () . Фактически, действительно нет никаких практических причин не использовать QPC во всех случаях, когда вы все равно использовали бы GetTickCount ().

В документации говорится, что он возвращает количество миллисекунд, но на самом деле точность функции даже близко не к 1 миллисекунде. В зависимости от машины (а во время выполнения AFAIK это невозможно узнать) точность может составлять 40 миллисекунд или даже больше. Если вам нужна точность в 1 миллисекунду, вы должны использовать QueryPerformanceCounter () . Фактически, действительно нет никаких практических причин не использовать QPC во всех случаях, когда вы все равно использовали бы GetTickCount ().

1
ответ дан 18 December 2019 в 06:51
поделиться

Выбор времени компиляции API на основе целевой версии Windows блокирует встроенный исполняемый файл для этой версии и более новых. Это распространенный метод для проектов с открытым исходным кодом, ориентированных на * nix, где предполагается, что пользователь настроит исходный код для своей платформы и скомпилирует его для установки.

В Windows это не обычный метод, потому что это не так. Как правило, можно с уверенностью предположить, что у конечного пользователя вообще будет компилятор, не говоря уже о том, чтобы иметь дело с тонкостями создания проекта для сборки.

Часто просто использование старого API, присутствующего во всех версиях Windows, является достаточный ответ. Это тоже просто: вы просто игнорируете существование нового API.

Когда этого недостаточно, вы используете LoadLibrary () и GetProcAddress () , чтобы попытаться разрешить новый символ во время выполнения. Если это не может быть решено, вы должны вернуться к старому API.

Вот возможная реализация. Он обнаруживает первый вызов и попытки загрузить библиотеку и разрешить имя «GetTickCount64» . Во всех вызовах, если указатель на разрешенный символ не равен нулю, он вызывает его и возвращает результат. В противном случае он возвращается к старому 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,

13
ответ дан 18 December 2019 в 06:51
поделиться

Если вам нужно поддерживать до Vista, я бы придерживался только использования GetTickCount (). В противном случае вам придется реализовать код времени выполнения для проверки версии Windows и вызова GetTickCount () в версиях Windows до Vista и GetTickCount64 () в Vista и более поздних версиях. Поскольку они возвращают значения разного размера (ULONGLONG v DWORD), вам также потребуется отдельная обработка того, что они возвращают. Использование только GetTickCount () (и проверка на переполнение) будет работать в обеих ситуациях, тогда как использование GetTickCount64 (), когда оно доступно, увеличивает сложность вашего кода и удваивает объем кода, который вам нужно написать.

Придерживайтесь использования только GetTickCount (), пока не убедитесь, что ваше приложение больше не должно работать на компьютерах, предшествующих Vista.

0
ответ дан 18 December 2019 в 06:51
поделиться

Возможно, это хорошая замена для 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;
}
0
ответ дан 18 December 2019 в 06:51
поделиться
Другие вопросы по тегам:

Похожие вопросы: