Избавьтесь от аппаратных макросов во встроенном программном обеспечении - PullRequest
1 голос
/ 23 августа 2011

Я работал над встроенной программой, используя C.

Существует множество аппаратных макросов, таких как

#ifdef HardwareA 
do A 
#endif

Это не читабельно, и трудно покрыть все различные пути с помощью юнит-тестов.

Итак, я решил переместить код, связанный с аппаратным обеспечением, в папки архива и использовать макросы в make-файле, чтобы решить, какая папка архива связана. Как в коде ядра Linux.

Но когда я увидел ядро ​​Linux, я заметил, что в папках архива так много дубликатов.

Как они вносят изменения во все связанное оборудование, если в одном оборудовании обнаружена ошибка, но могут повлиять на все остальные?

Я думаю, что при таком способе неизбежно появятся дубликаты в базе кода.

У кого-нибудь есть опыт работы с этим типом проблемы?

Как выполнить модульное тестирование кода, в котором много аппаратных макросов?

Рефакторинг кода для удаления аппаратных макросов из исходного кода?

Ответы [ 4 ]

3 голосов
/ 23 августа 2011

Звучит так, как будто вы заменяете функцию, подобную этой:

somefunc()
{
    /* generic code ... */

    #ifdef HardwareA 
    do A 
    #endif

    /* more generic code ... */
}

с несколькими реализациями, по одной в каждой папке архива, например:

somefunc()
{
    /* generic code ... */

    /* more generic code ... */
}

somefunc()
{
    /* generic code ... */

    do A 

    /* more generic code ... */
}

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

somefunc()
{
    /* generic code ... */

    do_A();

    /* more generic code ... */
}

.., а затем внедрите do_A() в папках архива: на Аппаратном оборудовании A у него есть код для этого аппаратного обеспечения,а на другом оборудовании это пустая функция.

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

2 голосов
/ 23 августа 2011

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

Для некоторых функций в системе сборки определены CONFIG_GENERIC_*, которые также заменяют ненужные хуки архитектуры обычными версиями (часто без операций).Например, арка без FPU не нуждается в хуках для сохранения / восстановления состояния FPU при переключении контекста.

1 голос
/ 24 августа 2011

Я склонен перемещать специфичные для аппаратного обеспечения #defines в один заголовок для каждой платформы, а затем выбирать его в файле "platform.h", который включает все исходные файлы.

platform.h:

#if defined PLATFORM_X86_32BIT
#include "Platform_X86_32Bit.h"
#elsif defined PLATFORM_TI_2812
#include "Platform_TI_2812.h"
#else
#error "Project File must define a platform"
#endif

Специфичные для архитектуры заголовки будут содержать 2 вещи.

1) Typedefs для всех распространенных целочисленных размеров, например typedef short int16_t; Обратите внимание, что c99 указывает 'stdint.h', который имеет эти предопределенные значения. (Никогда не используйте необработанный int в переносимом коде).

2) Заголовки функций или макросы для всего конкретного аппаратного поведения. Извлекая все зависимости для функций, основная часть кода остается чистой:

  //example data receive function
  HW_ReceiverPrepare();
  HW_ReceiveBytes(buffer, bytesToFetch);
  isGood = (Checksum(buffer+1, bytesToFetch-1) == buffer[0])
  HW_ReceiverReset();

Тогда один специфичный для платформы заголовок может предоставить прототип для сложной HW_ReceiverPrepare() функции, тогда как другой просто определяет его с помощью #define HW_ReceiverPrepare()

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

1 голос
/ 23 августа 2011

Такого рода ад #ifdef определенно следует избегать, но, естественно, вы также хотите избежать дублирования кода. Я не утверждаю, что это решит все ваши проблемы, но я думаю, что самый большой шаг, который вы можете сделать, это изменить ваши #ifdef с #ifdef HardwareX на #ifdef HAVE_FeatureY или #ifdef USE_FeatureZ. То, что это позволяет вам делать, - это фактор знания того, какое оборудование / ОС / и т. Д. у целей есть какие функции / интерфейсы из всех ваших исходных файлов и в один заголовок, что позволяет избежать таких вещей, как:

#if defined(HardwareA) || (defined(HardwareB) && HardwareB_VersionMajor>4 || ...

делает ваши источники нечитаемыми.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...