Неправильно ли размещать встроенные функции в заголовках C?


Я создаю Проект C для нескольких компиляторов, некоторые из которых являются устаревшими компиляторами, которые, похоже, не имеют поддержки link time inlining, поэтому казалось логичным разместить функции static inline непосредственно в заголовках и фактически иметь каждую единицу перевода, имеющую свою собственную копию.

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

Однако один мой коллега сказал мне, что это необычная вещь, и я должен избегать ее. На данном этапе проекта Я, вероятно, все еще могу перестроить, поэтому я просто хотел бы подтвердить, есть ли какие-то проблемы, с которыми мы можем столкнуться в долгосрочной перспективе, если мы решим использовать заголовочные строки?
3 4

3 ответа:

Из n1570 (последний публичный проект C11), §6.7.4:

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

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

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

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

Как прокомментировал cmaster , вы можете достигнуть вида "ручная вставка " с функциональными макросами вместо переносимого решения.

Согласно комментариям к вопросу, определение функций static _inline в заголовочных файлах - это путь для этого конкретного компилятора, цитата из компилятора doc ясно об этом.

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

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

При этом семантика кажется одной и той же (из проекта 1256 для C99 или проекта 1570 для C11):

6.7.4 спецификаторы функций
...
Любая функция с внутренней связью может быть встроенной функцией. Для функции с наружной компоновка, применяются следующие ограничения: если функция объявлена с помощью встроенного спецификатор функции, то он также должен быть определен в той же самой единице перевода. Если все из объявления области видимости файла для функции в блоке преобразования включают встроенную функцию спецификатор без extern, то определение в этой единице перевода является встроенным определение. Встроенное определение не предоставляет внешнего определения для функции, и не запрещает Ан внешнее определение в другой единице перевода. Встроенное определение предоставляет альтернативу внешнему определению, которое переводчик может использовать для реализации любой вызов функции в той же самой единице перевода. Не указано, является ли вызов к функция использует встроенное определение или внешнее определение

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

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

Другими словами, если вы просто объявите его как inline aaa, символ aaa не будет виден снаружи.

Цитирую из 6.7.4 Function specifiers

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

Emacs использует эту технику встраивания в lisp.h, заголовок, который определяет объекты lisp, я цитирую из исходного кода emacs:

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