Встроенная функция компиляции
Я намереваюсь предоставить простые оболочки для API операционной системы, которые выдают исключения при возникновении ошибок. Эти обертки просты, и все они определены как встроенные функции в заголовочном файле. Поскольку системный API должен быть большим, заголовочный файл также должен быть огромным и содержать большое количество крошечных встроенных функций. Проблема в том, что если общая библиотека (.so) скомпилирована с включенным заголовочным файлом, все эти крошечные обертки будут скомпилированы в результирующий двоичный файл, что приведет к большому двоичному файлу, даже если на самом деле только небольшая часть оберток используемый? А как насчет случая с исполняемыми файлами, будет ли он другим? Если это так, будет ли единственным решением проблемы разделение оболочек на несколько заголовочных файлов? Или я должен сделать внутреннюю связь оболочек, указав static
?
Вот что я думаю. Обертки могут использоваться ODR (например, принимая его адрес). А на платформах Linux функции с внешней связью экспортируются по умолчанию (т. Е. Связываются другими бинарными модулями). Поэтому я думаю, что для компоновщика может потребоваться сгенерировать определения структуры для них. Пожалуйста, обратитесь к пункту 3) в разделе " Описание " здесь.
Простой пример упаковки CloseHandle()
в Windows API:
inline void close_handle(HANDLE handle) {
if (!CloseHandle(handle)) {
throw std::system_error(GetLastError(), std::system_category(), "CloseHandle");
}
}
1 ответ
(Довольно маленькая) объявленная функция static inline
(или часто просто inline
или даже функция-член, определенная внутри некоторого class
или же struct
) не будет (на практике) появляться в коде, если он не используется (см. это), и, вероятно, будет вставлен везде. Конечно, вам нужно включить оптимизацию в вашей команде компиляции. Итак, если вы используете GCC, скомпилируйте g++ -Wall -O2
(и вы могли бы добавить -fverbose-asm -S
и загляните внутрь сгенерированного ассемблерного кода для проверки).
Некоторые компиляторы (и, вероятно, g++
) не будет беспокоиться о встраивании, если не будет предложено оптимизировать. А встраивание - это всегда оптимизация, которую компилятор может не делать в некоторых случаях (в частности, когда где-то хранится адрес этой функции).
Кстати, может показаться, что вы заново изобретаете фреймворк, похожий на POCO или Qt. Вы решили использовать их вместо этого?
Кроме того, последние реализации C++11 (и C++14) уже оборачивают значительную часть API-интерфейса ОС (в частности, стандартную библиотеку ввода-вывода C++ и библиотеку поддержки потоков C++ и, в последнее время, C++ 14 TS), часто уже используя исключения, так что лучше использовать их и использовать недавний компилятор C++ (для GCC это означает GCC 5.2 в ноябре 2015 года).
(Другими словами, используйте код как минимум для C++ 11, а не для C++98)
В Linux с GCC (или Clang / LLVM), если вы создаете библиотеку, вас может заинтересовать оптимизация времени соединения (скомпилируйте и скомпонуйте библиотеку с помощью g++ -O2 -flto
), предварительно скомпилированные заголовки, атрибуты функции видимости.
Что касается библиотек программ в Linux, прочитайте раздел "Библиотека программ". Что касается общих библиотек, прочитайте статью Дреппера " Как писать общие библиотеки" и этот ответ.
На практике встроенная функция в некоторой библиотеке часто не выделяется в библиотеке, а в прикладной программе, вызывающей ее. Так что если ваша общая библиотека Foo определяет в публичном заголовке <foo.h>
inline int maxsq(int a, int b) {
// you could add some conditional throw here...
if (std::abs(a) < std::abs(b)) return b*b;
else return a*a;
}
тогда объектный код maxsq
вероятно, не появится внутри libfoo.so
, но только в вашей программе (которая #include <foo.h>
в исходном коде), если эта программа нуждается maxsq
например, хранит адрес maxsq
где-то (или если вы не просили достаточно оптимизации).
Помните, что встраивание - это всегда оптимизация, и некоторые компиляторы могут иногда этого избегать (даже по хорошим причинам производительности). На практике доверяйте своему компилятору (фактически, ваша реализация C++ также включает компоновщик, который может "собирать мусор" в разделах).