Сделать tolower как статичный - PullRequest
1 голос
/ 30 марта 2012

Мне нужно сделать стандартную библиотечную функцию tolower статической, а не "публичной" областью действия.

Я компилирую с MISRA C: 2004, используя компилятор IAR Embedded Workbench. Компилятор объявляет tolower как встроенный:

  inline
  int tolower(int _C)
  {
    return isupper(_C) ? (_C + ('A' - 'a')) : _C;
  }

Я получаю следующую ошибку от компилятора:

Error[Li013]: the symbol "tolower" in RS232_Server.o is public but
is only needed by code in the same module - all declarations at
file scope should be static where possible (MISRA C 2004 Rule 8.10)  

Вот мои предложенные решения:

  1. Используйте tolower в другом модуле, в фиктивных обстоятельствах, чтобы он необходимо более чем для одного модуля.
  2. Реализация функциональности без использования tolower. Это встроенная система.
  3. Добавить макрос "STATIC", который по умолчанию определен как пустой, но может быть определяется как static до включения заголовочного файла ctype.h.

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

Редактировать 1:

Компилятор - IAR Embedded Workbench 6.30 для процессора ARM.
Я использую процессор ARM7TDMI в 32-битном режиме (не в режиме Thumb).
Функция tolower используется с портом отладки.

Редактировать 2:

Я также получаю ошибку для _LocaleC_isupper и _LocaleC_tolower

Решение:

  1. Я уведомил поставщика о проблеме в соответствии с рекомендациями Майкла Берра.
  2. Я решил не переписывать библиотеку из-за локализации вопросы.
  3. Я реализовал указатель на функцию в файле main.c, как было предложено gbulmer; Однако это будет прокомментировано невероятно, потому что это должно быть удаленным после того, как IAR решит их проблему.

Ответы [ 3 ]

2 голосов
/ 30 марта 2012

Я бы посоветовал вам отключить эту конкретную проверку MISRA (вы должны сделать это только для единицы перевода RS232_Server), а не использовать один из предложенных вами обходных путей. На мой взгляд, полезность правила 8.10 довольно минимальна, и перепрыгивание через обручи в предложенных обходных путях, скорее всего, представляет более высокий риск, чем просто отключение правила. Имейте в виду, что цель MISRA - снизить вероятность ошибок в коде на языке C.

Обратите внимание, что MISRA признает, что "в некоторых случаях может возникнуть необходимость отклониться от правил" и что есть документально подтвержденная "процедура отклонения" (раздел 4.3.2 в MISRA-C 2004).

Если вы не хотите или не можете отключить правило по какой-либо причине, по моему мнению, вам, вероятно, следует просто переопределить функциональность tolower() в своей собственной функции, особенно если вам не приходится иметь дело с поддержкой локали , Также может быть целесообразно открыть инцидент поддержки с IAR. Вы можете подтолкнуть их с помощью правила 3.6, гласящего, что «Все библиотеки, используемые в производственном коде, должны быть написаны для соответствия [MISRA-C]».

1 голос
/ 02 апреля 2012

Прежде всего, MISRA-C: 2004 не допускает ни inline, ни C99 .Предстоящий MISRA 2012 позволит это.Если вы попытаетесь запустить C99 или нестандартный код через статический анализатор MISRA-C: 2004, все ставки будут отключены.Средство проверки MISRA должно выдавать ошибку для встроенного ключевого слова.

Я считаю, что версия кода, совместимая с MISRA-C, будет выглядеть следующим образом:

static uint8_t tolower(uint8_t ch)
{
  return (uint8_t)(isupper(ch) ? (uint8_t)((uint8_t)(ch + 'A') - 'a') : 
                                 ch);
}

Некоторые комментарии по этому поводу: MISRA рекомендуетchar тип для символьных литералов, но в то же время предостерегает от использования простого типа char, поскольку он имеет подпись, определяемую реализацией.Поэтому я использую вместо этого uint8_t.Я полагаю, что глупо предполагать, что существуют таблицы ASCII с отрицательными индексами.

(_C + ('A' - 'a')), безусловно, не соответствует требованиям MISRA, поскольку MISRA считает, что он содержит два неявных продвижения типов.MISRA рассматривает символьные литералы как char, а не int, как стандарт C.

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

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

static uint8_t tolower (uint8_t ch)
{
  if(isupper(ch))
  {
    ch = (uint8_t)(ch - 'A');
    ch = (uint8_t)(ch + 'a');
  }
  return ch;
}
1 голос
/ 30 марта 2012

Кто продает компоновщик MISRA?Кажется, есть безумная ошибка.

Можете ли вы обойти это, взяв его адрес int (*foo)(int) = tolower; в области видимости файла?

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

Чтобы это было правильным поведением, то есть не ошибкой, было бы ошибкой MISRA включить один раз библиотечную функцию, например initialise_system_timer, initialise_watchdog_timer, ..., которая просто ранит мою голову.

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

Теория: Может быть, компилятор выполняет как встроенное, , так и , генерируя реализацию функции.Тогда функция (конечно) не вызывается.Таким образом, правила компоновщика видят эту не вызванную функцию.

GNU имеет опции для предотвращения встраивания.Можете ли вы сделать то же самое для этого использования Tolower?Это меняет ошибку?Вы можете выполнить тест с помощью

#define inline /* nothing */

перед включением ctype.h, а затем отменить определение макроса.

Другой тест должен определить:

#define inline static inline

перед включениемctype.h, которая является версией inline, которую я ожидаю.

EDIT2: Я думаю, что есть ошибка, о которой следует сообщить.Я полагаю, у IAR есть обходной путь.Я бы принял их совет.

После ночного сна я сильно подозреваю, что проблема inline int tolower(), а не static inline int tolower или int tolower, потому что это имеет больше смысла.Наличие общедоступной функции, которая не вызывается, похоже, является признаком ошибки в программе.

Даже с документацией все подходы к кодированию имеют свои недостатки.

  1. Я настоятельно поддерживаю OP, я бы не стал менять стандартный заголовочный файл.Я думаю, что есть несколько причин.Например, дальнейшее обновление цепочки инструментов (которая поставляется с новым набором заголовков) разрушает старое приложение, если оно когда-либо будет поддерживаться.Или просто сборка приложения на другом компьютере дает ошибку, объединение двух, по-видимому, правильных приложений может привести к ошибке, ....Ничего хорошего из этого не получится.-100

  2. Используйте #define ..., чтобы ошибка исчезла.Я не люблю использовать макросы для изменения стандартных библиотек.Это похоже на семена долгосрочной плохой идеи.Если в будущем другая часть программы использует другую функцию с аналогичной проблемой, применение «исправления» ухудшается.Некоторые неопытные разработчики могут взять на себя работу по поддержке кода, и «учится» оборачивать странные кусочки #define хитрости вокруг #include - это «нормальная» практика.Компания становится «практикой», чтобы заключить #include <ctype.h> в странные макро-обходные пути, которые остаются спустя годы после того, как это было исправлено.-20

  3. Используйте параметр компилятора командной строки, чтобы отключить встраивание. Если это работает и семантика верна, то есть включение заголовка и использование функции в двух исходных файлах не приводит к множественной определенной функции, тогда все в порядке. Я ожидаю, что это приведет к ошибке, но это стоит подтвердить как часть сообщения об ошибке. Это расстраивает ловушку для кого-то еще, кто придет в будущем. Если используется другая стандартная библиотечная функция inline, и по какой-то причине человек должен посмотреть на сгенерированный код, он также не будет включен. Они могут немного сойти с ума, задаваясь вопросом, почему встроенный не соблюдается. Я предполагаю, что причина, по которой они смотрят на сгенерированный код, в том, что производительность критична. По моему опыту, люди потратили много времени, глядя на код, сбитые с толку, прежде чем смотреть на скрипт сборки для программы, которая работает. Если подавление inline используется как исправление, я чувствую, что лучше делать это везде, чем для одного файла. По крайней мере, семантика является последовательной, и могут быть замечены комментарии или документация высокого уровня. Любой, кто использует сценарии сборки в качестве «быстрой проверки», будет иметь согласованное поведение, которое может побудить их взглянуть на документацию. -1 (везде без встроенного) -3 (без встроенного файла)

  4. Фальшиво используйте tolower во втором исходном файле. Это имеет небольшое преимущество в том, что система сборки не «заражена» дополнительными опциями компилятора. Также это небольшой файл, который даст возможность объяснить ошибку, которую можно обойти. Мне это не очень нравится, но мне нравится больше, чем возиться со стандартными заголовками. Моя текущая проблема - это может не сработать. Это может включать две копии, которые компоновщик не может разрешить. Мне нравится, что это лучше, чем странное «взять его адрес и посмотреть, не отключится ли компоновщик» (что, на мой взгляд, является интересным способом проверки крайних случаев). -2

  5. Кодируйте свой собственный tolower. Я не понимаю более широкий контекст приложения. Моя реакция , а не на замену кода для библиотечных функций, потому что я обеспокоен тестированием (и юнит-тестами, которые вводят еще больше кода) и долгосрочным обслуживанием. Я еще больше нервничаю с символьным вводом / выводом, потому что приложение может нуждаться в способности обрабатывать более широкий набор символов, например кодировку UTF-8, и пользовательский tolower будет иметь тенденцию выходить из синхронизации. Это звучит как очень специфическое приложение (отладка на порт), так что я мог справиться. Мне не нравится писать дополнительный код для работы с вещами, которые выглядят как ошибки, особенно если это коммерческое программное обеспечение. -5

  6. Можете ли вы преобразовать ошибку в предупреждение, не потеряв все остальные проверки? Я все еще чувствую, что это ошибка в инструментальной цепочке, поэтому я предпочел бы, чтобы это было предупреждение, а не молчание, чтобы была ловушка для инцидента и некоторой документации, и меньше вероятность появления другой ошибки. В противном случае идти с выключением ошибки. +1 (предупреждение) 0 (ошибка)

Выключение ошибки, по-видимому, теряет «корпоративную осведомленность» о том, что (IMHO) IAR обязан вам объяснением и более долгосрочным исправлением, но это кажется намного лучше, чем возиться с системой сборки, записывая макро-мерзость в соответствии со стандартом библиотеки, или написание собственного кода, который увеличивает ваши расходы.

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

...