Я в настоящее время работаю над phpDataMapper, который является ORM, разработанным для имения простого синтаксиса как проект Ruby Datamapper. Это находится все еще в ранней разработке также, но это работает отлично.
Hard to maintain. Better use interfaces to abstract platform specific code than abusing conditional compilation by scattering #ifdef
s all over your implementation.
E.g.
void foo() {
#ifdef WIN32
// do Windows stuff
#else
// do Posix stuff
#endif
// do general stuff
}
Is not nice. Instead have files foo_w32.c
and foo_psx.c
with
foo_w32.c:
void foo() {
// windows implementation
}
foo_psx.c:
void foo() {
// posix implementation
}
foo.h:
void foo(); // common interface
Then have 2 makefiles1: Makefile.win
, Makefile.psx
, with each compiling the appropriate .c
file and linking against the right object.
Minor amendment:
If foo()
's implementation depends on some code that appears in all platforms, E.g. common_stuff()
2, simply call that in your foo()
implementations.
E.g.
common.h:
void common_stuff(); // May be implemented in common.c, or maybe has multiple
// implementations in common_{A, B, ...} for platforms
// { A, B, ... }. Irrelevant.
foo_{w32, psx}.c:
void foo() { // Win32/Posix implementation
// Stuff
...
if (bar) {
common_stuff();
}
}
While you may be repeating a function call to common_stuff()
, you can't parameterize your definition of foo()
per platform unless it follows a very specific pattern. Generally, platform differences require completely different implementations and don't follow such patterns.
make
at all, such as if you use Visual Studio, CMake, Scons, etc.common_stuff()
actually has multiple implementations, varying per platform.(В некоторой степени не соответствует заданному вопросу )
Однажды я увидел совет, предлагающий использовать блоки #if (n) def / # endif
для использования в отладке / изолировании кода вместо комментирования.
Это было предложено, чтобы помочь избежать ситуаций в котором комментируемый раздел уже содержит комментарии к документации, и должно быть реализовано решение вроде следующего:
/* <-- begin debug cmnt if (condition) /* comment */
/* <-- restart debug cmnt {
....
}
*/ <-- end debug cmnt
Вместо этого это будет:
#ifdef IS_DEBUGGED_SECTION_X
if (condition) /* comment */
{
....
}
#endif
Мне показалось отличной идеей. Хотел бы я запомнить источник и связать его: (
Because then when you do search results you don't know if the code is in or out without reading it.
Because they should be used for OS/Platform dependencies, and therefore that kind of code should be in files like io_win.c or io_macos.c
Моя интерпретация этого правила: На логику вашей (алгоритмической) программы не должны влиять определения препроцессора. Работа вашего кода всегда должна быть лаконичной. Любая другая форма логики (платформа, отладка) должна абстрагироваться в файлах заголовков.
Это скорее руководство, чем строгое правило, ИМХО. Но я согласен с тем, что решения на основе c-синтаксиса предпочтительнее препроцессорной магии.
Совет сохранять условные выражения препроцессора в файлах заголовков - это хорошо, поскольку позволяет выбирать интерфейсы условно, но не засорять код запутанной и уродливой логикой препроцессора. .
Тем не менее, есть много-много-много кода, который выглядит как выдуманный пример ниже, и я не думаю, что есть явно лучшая альтернатива. Я думаю, вы процитировали разумное руководство, но не отличную заповедь о золотой таблетке.
#if defined(SOME_IOCTL)
case SOME_IOCTL:
...
#endif
#if defined(SOME_OTHER_IOCTL)
case SOME_OTHER_IOCTL:
...
#endif
#if defined(YET_ANOTHER_IOCTL)
case YET_ANOTHER_IOCTL:
...
#endif
CPP - это отдельный (неполный по Тьюрингу) макроязык поверх (обычно) C или C ++. Таким образом, его легко перепутать с базовым языком, если вы не будете осторожны. Во всяком случае, это обычный аргумент против макросов, а не, например, шаблонов C ++. Но #ifdef? Просто попробуйте прочитать чей-то код, который вы никогда раньше не видели, в котором есть куча ifdef.
например, попробуйте прочитать эти функции Рида-Соломона умножить блок на постоянное значение Галуа: http://parchive.cvs.sourceforge.net/viewvc/parchive/par2-cmdline/reedsolomon.cpp?revision=1.3&view=markup
Если у вас не было следующей подсказки, вам будет предложено минута, чтобы понять, что происходит: есть две версии: одна простая, а другая с предварительно вычисленной таблицей поиска (LONGMULTIPLY). Тем не менее, получайте удовольствие, отслеживая #if BYTE_ORDER == __LITTLE_ENDIAN. Мне стало намного легче читать, когда я переписал этот бит для использования функции le16_to_cpu (определение которой было внутри предложений #if), вдохновленных материалом Linux byteorder.h.
Если вам нужно другое низкоуровневое поведение в зависимости от build, попробуйте инкапсулировать это в низкоуровневых функциях, которые обеспечивают единообразное поведение повсюду, вместо того, чтобы помещать материал #if прямо в ваши более крупные функции.
Если ваш код будет скомпилирован с разными компиляторами C, и вы используете специфичные для компилятора функции, то вам может потребоваться определить, какие предопределенные макросы доступны.
Обязательно отдавайте предпочтение абстракции над условной компиляцией. Однако любой, кто написал портативное программное обеспечение, может сказать вам, что количество изменений среды ошеломляет. Некоторая дизайнерская дисциплина может помочь, но иногда выбор стоит между элегантностью и соблюдением графика. В таких случаях может потребоваться компромисс.
Условную компиляцию трудно отладить. Чтобы понять, какой блок кода программа будет выполнять, нужно знать все настройки.
Однажды я потратил неделю на отладку многопоточного приложения, в котором использовалась условная компиляция. Проблема заключалась в том, что идентификатор был написан по-разному. Один модуль использовал #if FEATURE_1
, в то время как проблемная область использовала #if FEATURE1
(обратите внимание на подчеркивание).
Я большой сторонник того, чтобы make-файл
обрабатывал конфигурацию, включая правильные библиотеки или объекты. Делает код более читаемым. Кроме того, большая часть кода становится независимой от конфигурации, и только несколько файлов зависят от конфигурации.
Это правда, что #if #endif действительно усложняет чтение кода. Однако я видел много реального кода, у которого нет проблем с его использованием, и который все еще работает. Так что могут быть более эффективные способы избежать использования #if #endif, но их использование не так уж и плохо при надлежащей осторожности.