Неправильно ли размещать встроенные функции в заголовках C?
Я создаю Проект C для нескольких компиляторов, некоторые из которых являются устаревшими компиляторами, которые, похоже, не имеют поддержки link time inlining, поэтому казалось логичным разместить функции static inline
непосредственно в заголовках и фактически иметь каждую единицу перевода, имеющую свою собственную копию.
Кроме того, мне нужно убедиться, что некоторые функции встроены так, чтобы не было вызовов других функций (т. е. изменений регистров процессора) при вызове внутри определенных низкоуровневых обработчиков прерываний, так что это не просто пусть компилятор выбирает, повлияет ли это на производительность.
Однако один мой коллега сказал мне, что это необычная вещь, и я должен избегать ее. На данном этапе проекта Я, вероятно, все еще могу перестроить, поэтому я просто хотел бы подтвердить, есть ли какие-то проблемы, с которыми мы можем столкнуться в долгосрочной перспективе, если мы решим использовать заголовочные строки?3 ответа:
Из n1570 (последний публичный проект C11), §6.7.4:
- функция, объявленная с помощью спецификатора функции
inline
, является встроенной функцией . Изготовление функция встроенная функция предполагает, что вызовы функции должны выполняться как можно быстрее. Степень эффективности таких предложений определяется их реализацией.В следующем разделе подробно рассматривается связь, но этот отрывок выше-это в основном все, что стандарт C может сказать о
Таким образом, при использовании только стандартного языка Си можно получить несколько экземпляров (по одному на единицу перевода) функции, вызываемой обычным способом. Обычно это не то, что вы хотите, так как он сочетает в себе два недостатка (дублированный код и накладные расходы вызова функции). Поэтому я бы сказал, что стандартный Cinline
. Обратите внимание, как это дает реализации любую свободу, в том числе полностью игнорироватьinline
.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 без оптимизации.
нет необходимости встраивать все, только операции, которые в противном случае вызвали бы серьезные проблемы с производительностью.