C++: зависимые типы Платформы - лучший [закрытый] шаблон

21
задан Joe Schneider 16 July 2010 в 15:24
поделиться

6 ответов

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

Есть несколько инструментов, способных выполнить эту задачу, например autotools , Scons или Cmake .

В вашем случае я бы рекомендовал использовать CMake, так как он прекрасно интегрируется с Windows, имея возможность генерировать файлы проекта Visual Studio, а также make-файлы Mingw.

Основная философия, лежащая в основе этих инструментов, заключается в том, что вы не тестируете заново саму ОС, а проверяете возможности, которые могут присутствовать или отсутствовать, или для которых значения могут варьироваться, что снижает риск того, что ваш код не будет скомпилирован с " платформа не поддерживается »ошибка.

Вот прокомментированный образец CMake (CMakeFiles.txt):

# platform tests
include(CheckFunctionExists)
include(CheckIncludeFile)
include(CheckTypeSize)
include(TestBigEndian)

check_include_file(sys/types.h  HAVE_SYS_TYPES_H)
check_include_file(stdint.h     HAVE_STDINT_H)
check_include_file(stddef.h     HAVE_STDDEF_H)
check_include_file(inttypes.h   HAVE_INTTYPES_H)

check_type_size("double"      SIZEOF_DOUBLE)
check_type_size("float"       SIZEOF_FLOAT)
check_type_size("long double" SIZEOF_LONG_DOUBLE)

test_big_endian(IS_BIGENDIAN)
if(IS_BIGENDIAN)
  set(WORDS_BIGENDIAN 1)
endif(IS_BIGENDIAN)

# configuration file
configure_file(config-cmake.h.in ${CMAKE_BINARY_DIR}/config.h)
include_directories(${CMAKE_BINARY_DIR})
add_definitions(-DHAVE_CONFIG_H)

При этом вы должны предоставить шаблон config-cmake.h.in, который будет обработан cmake для создания файла config.h, содержащего определения. вам нужно:

/* Define to 1 if you have the <sys/types.h> header file. */
#cmakedefine HAVE_SYS_TYPES_H
/* Define to 1 if you have the <stdint.h> header file. */
#cmakedefine HAVE_STDINT_H
/* Define to 1 if your processor stores words with the most significant byte
   first (like Motorola and SPARC, unlike Intel and VAX). */
#cmakedefine WORDS_BIGENDIAN
/* The size of `double', as computed by sizeof. */
#define SIZEOF_DOUBLE @SIZEOF_DOUBLE@
/* The size of `float', as computed by sizeof. */
#define SIZEOF_FLOAT @SIZEOF_FLOAT@
/* The size of `long double', as computed by sizeof. */
#define SIZEOF_LONG_DOUBLE @SIZEOF_LONG_DOUBLE@

Я приглашаю вас посетить веб-сайт cmake, чтобы узнать больше об этом инструменте.

Я лично фанат cmake, который использую в своих личных проектах.

18
ответ дан 29 November 2019 в 21:21
поделиться

Один из способов сделать это - поместить заголовки, специфичные для ОС, в разные каталоги и контролировать, какой каталог будет найден, передавая его компилятору с помощью флага -I.

В вашем случае вы бы просто

#include "defs.h"
6
ответ дан 29 November 2019 в 21:21
поделиться

В использовании препроцессора нет ничего "плохого" по своей сути. Он был создан по какой-то причине, и такая условная компиляция / включение - одна из вещей, которые работают довольно хорошо.

Главное - сохранить читабельность вашего кода. Если в вашем коде много блоков #ifdef WIN32 или вы обнаруживаете, что делаете это в большом количестве файлов, вот несколько советов, как немного очистить ваш код.

1) Переместите свое условное включение в отдельный файл. Ваши исходные файлы будут просто #include и переместят ваш блок условного включения в defs.h (даже если это все, что есть в этом файле).

2) Пусть ваш make-файл определяет, какой файл включить. Опять же, ваши исходные файлы будут просто #include . Ваш make-файл определит текущую платформу и при необходимости добавит -iwin32 или -ilinux в строку параметров компилятора.Я обычно придерживаюсь этого подхода. Вы можете выполнить работу по обнаружению платформы внутри make-файла или на этапе настройки (если вы динамически генерируете свои make-файлы). Я также считаю, что этот вариант является самым простым с точки зрения добавления поддержки новой платформы позднее; кажется, это сводит к минимуму количество необходимых изменений.

3) Если вы строите в системе Linux (подразумевая, что любой код Win32 будет сгенерирован кросс-компилятором), вы можете создать в сценариях сборки символическую ссылку на соответствующий каталог файла заголовка. Ваш код может ссылаться на каталог как #include и позволить make-файлу или скрипту configure позаботиться о символической ссылке на правильную папку.

4
ответ дан 29 November 2019 в 21:21
поделиться

Препроцессор сам по себе неплох. Это мощный инструмент, как и многие другие инструменты разработки. Вопрос в том, как вы его используете?

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

В идеале вы должны ограничить, как часто вы это делаете. Так что, возможно, у вас есть defs.h , который вы включаете во все свои файлы, и внутри этого одного файла вы выполняете логику, специфичную для вашей ОС.

2
ответ дан 29 November 2019 в 21:21
поделиться

Если вы собираетесь использовать препроцессор, то сейчас самое время его использовать. Существуют различные решения, но я считаю, что вы можете решить эту проблему чистым способом.

Обычно я решаю эту проблему так: сначала создаю заголовок, который определяет платформу и компилятор, например:

#define LIB_PLATFORM_WIN32 (1)
#define LIB_PLATFORM_LINUX (2)

#if defined(WIN32)
   #define LIB_PLATFORM LIB_PLATFORM_WIN32
#elif defined(LINUX)
   #define LIB_PLATFORM LIB_PLATFORM_LINUX
#endif

Теперь у вас есть платформа; затем вы можете просто использовать свои собственные определения во всем коде.

Например, если вам нужно независимое от компилятора 64-битное целое число, вы можете использовать это

#if LIB_COMPILER == LIB_COMPILER_MSVC
    #define int64   __int64
    #define uint64  unsigned __int64
 #else
    #define int64   long long
    #define uint64  unsigned long long
#endif

Если вы хорошо структурируете его, у вас не должно быть проблем с сохранением чистоты и здравомыслия. :)

Вы также можете использовать typedefs, но это может вызвать другие проблемы, если вы захотите перегрузить тип позже в вашем приложении.

0
ответ дан 29 November 2019 в 21:21
поделиться

Это действительно зависит от детализации фактических различий между реализациями для конкретных платформ (в вашем коде это соответствует "common stuff below here").

Когда различия значительны, я часто предпочитаю разложить весь платформозависимый код на несколько компонентов (.cpp), имеющих один и тот же платформонезависимый интерфейс (.hpp).

Например, предположим, нам нужно получать изображения с камеры firewire в кроссплатформенной прикладной программе. Способы достижения этой цели на разных платформах безнадежно отличаются. Тогда имеет смысл иметь общий интерфейс, который будет помещен в CameraIeee1394.hpp, а две платформозависимые реализации разместить в CameraIeee1394_unix.cpp и CameraIeee1394_w32.cpp.

Скрипт, подготавливающий makefile для конкретной платформы, может автоматически игнорировать исходные файлы для других платформ, просто проверяя суффикс имени файла.

1
ответ дан 29 November 2019 в 21:21
поделиться
Другие вопросы по тегам:

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