Альтернативы директивам препроцессора - PullRequest
6 голосов
/ 13 мая 2009

Я занимаюсь разработкой приложения для мобильного телефона C ++ на платформах Symbian. Одним из требований является то, что он должен работать на всех телефонах Symbian, начиная с телефонов 2-го издания до телефонов 5-го издания. Теперь между версиями есть различия в Symbian SDK. Я должен использовать директивы препроцессора для условной компиляции кода, относящегося к SDK, для которого создается приложение, как показано ниже:

#ifdef S60_2nd_ED
  Code
#elif S60_3rd_ED
  Code
#else
  Code

Теперь, поскольку приложение, которое я разрабатываю, не является тривиальным, оно скоро вырастет до десятков тысяч строк кода, и директивы препроцессора, как указано выше, будут распространяться повсеместно. Я хочу знать, есть ли альтернатива этому или может быть лучший способ использовать эти директивы препроцессора в этом случае.

Пожалуйста, помогите.

Ответы [ 9 ]

15 голосов
/ 13 мая 2009

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

У вас будет

  • MyClass.h
  • MyClass_S60_2nd.cpp
  • MyClass_S60_3rd.cpp

и так далее. Вы можете выбрать, какой файл CPP скомпилировать, обернув все внутри, используя #ifdefs, как указано выше, или мой контроль на уровне сборки (через Makefiles или любой другой), какие файлы включаются при сборке для различных целей.

В зависимости от характера изменений, это может быть намного чище.

6 голосов
/ 13 мая 2009

В нашей компании мы пишем много кроссплатформенного кода (разработка игр для win32 / ps3 / xbox / etc).
Чтобы максимально избежать макросов, связанных с платформой, мы обычно используем следующие несколько приемов:

  • извлекать связанный с платформой код в библиотеки абстракции платформы, которые имеют одинаковый интерфейс на разных платформах, но не одну и ту же реализацию;
  • разбить код на разные файлы .cpp для разных платформ (например: "pipe.h", "pipe_common.cpp", "pipe_linux.cpp", "pipe_win32.cpp", ...);
  • использовать макросы и вспомогательные функции для объединения вызовов функций, специфичных для платформы (например: "#define usleep (X) Sleep ((X) / 1000u)");
  • использовать кроссплатформенные сторонние библиотеки.
5 голосов
/ 13 мая 2009

Я был именно там, где вы есть.

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

#if S60_3rd_ED
    #define CAF_AGENT 1
    #define HTTP_FILE_UPLOAD 1
#elif S60_2nd_ED
    #define CAF_AGENT 0
    #if S60_2nd_ED_FP2
        #define HTTP_FILE_UPLOAD 1
    #else
        #define HTTP_FILE_UPLOAD 0
    #endif
#endif

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

У нас также есть определения для классов пользовательского интерфейса, от которых вы наследуете, поэтому между S60 и UIQ был некоторый общий код пользовательского интерфейса. Фактически из-за того, что это был за продукт, у нас не было большого количества кода, связанного с пользовательским интерфейсом, поэтому приличная его доля была обычной.

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

[Изменить в ответ на комментарий:

Мы очень старались избегать каких-либо действий, зависящих от разрешения - к счастью, конкретное приложение не усложняло задачу, поэтому наш ограниченный пользовательский интерфейс был довольно универсальным. Главное, где мы включили разрешение экрана, были заставки / фоновые изображения и тому подобное. У нас был скрипт для предварительной обработки файлов сборки, который заменял ширину и высоту на имя файла, splash_240x320.bmp или что-то еще. На самом деле мы создавали изображения вручную, поскольку их было не так много, а изображения менялись не часто. Тот же сценарий сгенерировал файл .h, содержащий #defines большинства значений, используемых при создании файла сборки.

Это для сборок на устройство: у нас также были более общие файлы SIS, которые просто меняли размеры на лету, но у нас часто были требования к установленному размеру (иногда ПЗУ было довольно ограниченным, что имеет значение, если ваше приложение является частью Базовое изображение устройства), и изменение размера изображений было одним из способов немного уменьшить его. Для поддержки поворота экрана на N92, Z8 и т. Д. Нам по-прежнему требовались портретная и альбомная версии некоторых изображений, поскольку изменение соотношения сторон не дает столь же хороших результатов, как изменение размера до того же или аналогичного соотношения ...]

1 голос
/ 13 мая 2009

Существует несколько различий между приложениями S60 2nd ed и 3rd ed, которые не ограничиваются кодом: файлы ресурсов приложения различаются, графические форматы и инструменты для их упаковки различаются, mmp-файлы различаются во многих отношениях.

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

1 голос
/ 13 мая 2009

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

1 голос
/ 13 мая 2009

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

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

Таким образом, вы будете иметь директиву выбора платформы в меньшем количестве мест в вашем коде (в идеале, в одном месте, явно в заголовочном файле, объявляющем интерфейс).

Это означает что-то вроде:

commoninterface.h /* declaring the common interface API. Platform identification preprocessor directives might be needed for things like common type definitions */
platform1.c /*specific implementation*/
platform2.c /*specific implementation*/
0 голосов
/ 13 мая 2009

Вы должны стараться не распространять #ifs через код.

Скорее; используйте #if в заголовочных файлах для определения альтернативных макросов, а затем в коде используйте один макрос.

Этот метод позволяет сделать код немного более читабельным.

Пример:

 Plop.h
 ======

 #if V1
 #define    MAKE_CALL(X,Y)    makeCallV1(X,Y)
 #elif V2
 #define    MAKE_CALL(X,Y)    makeCallV2("Plop",X,222,Y)
 ....
 #endif


 Plop.cpp
 ========

 if (pushPlop)
 {
     MAKE_CALL(911,"Help");
 }

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

0 голосов
/ 13 мая 2009

Вы можете сделать что-то подобное для определения сборки в ядре Linux. Каждая архитектура имеет свой собственный каталог (например, asm-x86). Все эти папки объединяют одинаковые заголовочные файлы высокого уровня, представляющие один и тот же интерфейс. Когда ядро ​​настроено, создается ссылка с именем asm, предназначенная для соответствующего каталога asm-arch. Таким образом, все файлы C включают такие файлы, как.

0 голосов
/ 13 мая 2009

Нет идеи об альтернативе, но вы можете использовать разные файлы для включения в разные версии ОС. Пример

#ifdef S60_2nd_ED

#include "graphics2"

#elif S60_3rd_ED

#include "graphics3"

#else

#include "graphics"

...