Вопрос

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

Ниже приведен пример, но вот в чем вопрос.Я заржавел на своем C но я точно помню, что использование #ifdef не рекомендуется.Ребята, вы можете предложить альтернативу получше?Также - я думаю, что некоторые из них (если не все) могут быть установлены как переменная среды или переданы в качестве параметра, и если да - каков был бы хороший способ определить их, а затем получить доступ из исходного кода?

Вот фрагмент кода, с которым я имею дело

#define DAN          NO
#define UNIX         NO
#define LINUX        YES
#define WINDOWS_ES   NO
#define WINDOWS_RB   NO

/* Later in the code */
#if ((DAN==1) || (UNIX==YES))
#include <sys/param.h>
#endif

#if ((WINDOWS_ES==YES) || (WINDOWS_RB==YES) || (WINDOWS_TIES==YES))
#include <param.h>
#include <io.h>
#include <ctype.h>
#endif

/* And totally insane harcoded paths */
#if (DAN==YES)
char MasterSkipFile[MAXSTR] = "/home/dp120728/tools/testarea/test/MasterSkipFile";
#endif

#if (UNIX==YES)
char MasterSkipFile[MAXSTR] = "/home/tregrp/tre1/tretools/MasterSkipFile";
#endif

#if (LINUX==YES)
char MasterSkipFile[MAXSTR] = "/ptehome/tregrp/tre1/tretools/MasterSkipFile";
#endif

/* So on for every platform and combination */
Это было полезно?

Решение

Конечно, вы можете передать -DWHATEVER в командной строке. Или -DWHATEVER_ELSE = NO и т. Д. Может быть, для путей вы могли бы сделать что-то вроде

char MasterSkipFile[MAXSTR] = SOME_COMMAND_LINE_DEFINITION;

, а затем передать

-DSOME_COMMAND_LINE_DEFINITION="/home/whatever/directory/filename"

в командной строке.

Другие советы

Одна вещь, которую мы использовали, - это сгенерированный файл .h с этими определениями и сгенерируйте его с помощью скрипта. Это помогло нам избавиться от множества хрупких #ifs и #ifdefs

Вам нужно быть осторожным с тем, что вы там поместили, но параметры, подходящие для машины, являются хорошими кандидатами - так работает autoconf / automake.

РЕДАКТИРОВАТЬ: в вашем случае примером будет использование сгенерированного файла .h для определения INCLUDE_SYS_PARAM и INCLUDE_PARAM , а также в сам код использования:

#ifdef INCLUDE_SYS_PARAM
#include <sys/param.h>
#endif

#ifdef INCLUDE_PARAM
#include <param.h>
#endif

Упрощает перенос на новые платформы - существование новой платформы не влияет на код, а только на сгенерированный файл .h .

Специфичные для платформы заголовки конфигурации

У меня была бы система для генерации специфичной для платформы конфигурации в заголовок, который используется во всех сборках. Имя AutoConf - «config.h»; Вы можете увидеть «platform.h» или «porting.h» или «port.h» или другие варианты темы. Этот файл содержит информацию, необходимую для построения платформы. Вы можете сгенерировать файл, скопировав вариант, зависящий от версии, в стандартное имя. Вы можете использовать ссылку вместо копирования. Или вы можете запустить конфигурационные сценарии, чтобы определить его содержимое на основе того, что сценарий найден на компьютере.

Значения по умолчанию для параметров конфигурации

Код:

#if (DAN==YES)
char MasterSkipFile[MAXSTR] = "/home/dp120728/tools/testarea/MasterSkipFile";
#endif

#if (UNIX==YES)
char MasterSkipFile[MAXSTR] = "/home/tregrp/tre1/tretools/MasterSkipFile";
#endif

#if (LINUX==YES)
char MasterSkipFile[MAXSTR] = "/ptehome/tregrp/tre1/tretools/MasterSkipFile";
#endif

Было бы лучше заменить на:

#ifndef MASTER_SKIP_FILE_PATH
#define MASTER_SKIP_FILE_PATH "/opt/tretools/MasterSkipFile"
#endif

const char MasterSkipFile[] = MASTER_SKIP_FILE_PATH;

Те, кому нужна сборка в другом месте, могут установить местоположение с помощью:

-DMASTER_SKIP_FILE_PATH='"/ptehome/tregtp/tre1/tretools/PinkElephant"'

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

#ifndef DEFAULTABLE_PARAMETER
#define DEFAULTABLE_PARAMETER default_value
#endif

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

Перемещаемое программное обеспечение

Я не уверен насчет дизайна программного обеспечения, которое можно установить только в одном месте. В моей книге вы должны иметь возможность установить на машину старую версию 1.12 продукта одновременно с новой версией 2.1, и они должны работать независимо друг от друга. Жестко заданное имя пути побеждает это.

Параметризация по функции, а не по платформе

Ключевое различие между инструментами AutoConf и средней альтернативной системой заключается в том, что конфигурация выполняется на основе функций, а не на платформах. Вы параметризуете свой код, чтобы определить функцию, которую вы хотите использовать. Это очень важно, потому что функции, как правило, появляются на платформах, отличных от оригинала. Я смотрю после кода, где есть такие строки, как:

#if defined(SUN4) || defined(SOLARIS_2) || defined(HP_UX) || \
    defined(LINUX) || defined(PYRAMID) || defined(SEQUENT) || \
    defined(SEQUENT40) || defined(NCR) ...
#include <sys/types.h>
#endif

Было бы намного, намного лучше иметь:

#ifdef INCLUDE_SYS_TYPES_H
#include <sys/types.h>
#endif

А затем на платформах, где это необходимо, сгенерируйте:

#define INCLUDE_SYS_TYPES_H

(Не воспринимайте этот пример заголовка слишком буквально; это концепция, которую я пытаюсь преодолеть.)

Рассматривайте платформу как набор функций

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

Функции продукта должны быть включены в заголовок

( Разработка комментария к другому ответу. )

Предположим, у вас есть несколько функций в продукте, которые необходимо включить или исключить условно. Например:

KVLOCKING
B1SECURITY
C2SECURITY
DYNAMICLOCKS

Соответствующий код включается, когда установлено соответствующее определение:

#ifdef KVLOCKING
...KVLOCKING stuff...
#else
...non-KVLOCKING stuff...
#endif

Если вы используете инструмент анализа исходного кода, например cscope , то будет полезно, если он покажет вам когда KVLOCKING определен. Если единственное место, где он определен, находится в некоторых случайных файлах Makefile, разбросанных по системе сборки (предположим, что в этом документе используется сотня подкаталогов), трудно сказать, по-прежнему ли используется код на любом из ваши платформы. Если определения находятся где-то в заголовке - в заголовке для конкретной платформы или, возможно, в заголовке выпуска продукта (поэтому версия 1.x может иметь KVLOCKING, а версия 2.x может включать C2SECURITY, но 2.5 включает B1SECURITY и т. Д.), Тогда вы можете увидеть, что Код KVLOCKING все еще используется.

Поверьте мне, после двадцати лет разработки и текучести кадров люди не знают, используются ли функции по-прежнему или нет (потому что он стабилен и никогда не вызывает проблем - возможно, потому, что он никогда не используется). И если единственное место, где можно узнать, определен ли еще KVLOCKING, находится в файлах Makefile, то такие инструменты, как cscope, менее полезны, что делает изменение кода более подверженным ошибкам при попытке очистки позже.

Его гораздо разумнее использовать:

#if SOMETHING

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

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

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

Таким образом, в этом случае у вас будет #include " OS_Specific.h " в этом файле. Вы помещаете различные включения и определение MasterSkipFile для этой платформы. Вы можете выбрать между ними, указав различные -I (включая каталоги путей) в командной строке компилятора.

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

Я видел системы сборки, в которых большинство исходных файлов начинали примерно так:

#include PLATFORM_CONFIG
#include BUILD_CONFIG

и компилятор был запущен с помощью:

cc -DPLATFORM_CONFIG="linuxconfig.h" -DBUILD_CONFIG="importonlyconfig.h"

(может потребоваться экранирование от обратной косой черты)

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

Общие положения

Я еретик, который был изгнан из Церкви GNU Autotools.Почему?Потому что мне нравится понимать, что, черт возьми, делают мои инструменты.И потому, что у меня был опыт попыток объединить два компонента, каждый из которых настаивал на том, чтобы на моем компьютере по умолчанию была установлена другая, несовместимая версия autotools.

Я работаю, создавая один файл .h или .c для каждой комбинации платформы и значимой абстракции.Я усердно работаю над определением центрального файла .h, в котором указано, что интерфейс есть.Часто это означает, что я в конечном итоге создаю "уровень совместимости", который изолирует меня от различий между платформами.Часто я заканчиваю тем, что использую стандарт ANSI C, когда это возможно, вместо функциональности, зависящей от конкретной платформы.

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

Я восхищаюсь творчеством Гленна Фаулера nmake и Фонг Во iffe (если функция существует), которые, я думаю, спроектированы лучше, чем инструменты GNU.Но эти инструменты являются частью технологического пакета AT & T Software, и я не смог понять, как их использовать, не купившись на весь подход AST, который я не всегда понимаю.

Ваш пример

Там явно должно быть

extern char MasterSkipFile[];

где-нибудь в файле .h, и затем вы можете создать ссылку на подходящий файл .o.

Условное включение "правильного набора файлов .h для платформы" - это то, с чем я бы справился, пытаясь придерживаться ANSI C, когда это возможно, а когда это невозможно, определяя уровень совместимости в файле .h для конкретной платформы.Как бы то ни было, я не могу сказать, какие названия #includes пытаются импортировать, поэтому я не могу дать более конкретный совет.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top