Должен ли я предпочесть соглашение о вызовах по умолчанию __fastcall, когда меня на самом деле не волнует соглашение о вызовах?

#c #windows #visual-c #com #calling-convention

#c #Windows #visual-c #com #соглашение о вызовах

Вопрос:

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

Теперь я вижу функцию, которая вызывается не напрямую через COM, а скорее только из нашего кода C , и эта функция также имеет STDMETHODCALLTYPE макрос в своей подписи. Я полностью уверен, что макрос там бесполезен — никаких вызовов через COM к этой функции никогда не происходит.

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

Ответ №1:

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

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

Для экспортируемых функций вам явно нужно указать соглашение. Если вы создаете библиотеку, которая, как вы ожидаете, будет вызываться с языков, отличных от C или C , было бы обычным использовать stdcall . Если вы ожидаете только клиентов C или C , то cdecl, вероятно, является наиболее распространенным соглашением.

Комментарии:

1. На самом деле вам также нужно указать библиотеки соглашений о вызовах — вы не можете быть уверены, что ваш вызывающий абонент всегда будет использовать те же настройки компилятора, что и вы. И что происходит, когда соглашение о вызовах компилятора по умолчанию меняется (так было в прошлом), и вы не перекомпилируете весь свой код?

2. @Larry Обычно, когда вы используете библиотеку, соглашение о вызовах определяет файл заголовка. Я принимал это как должное. Или я вас неправильно понял? Я имею в виду, что требование о том, что вы указываете явно при экспорте, применяется в равной степени при импорте. Но обычно при написании кода автор библиотеки записывает заголовочный файл импорта и связанный с ним . библиотека, а также сама DLL.

3. Нет, вы меня не неправильно поняли. Когда я прочитал ваш ответ, я подумал, что вы предлагаете библиотекам использовать соглашение о вызовах по умолчанию вместо явного указания соглашения о вызовах. Ключевым моментом путаницы является слово «модуль» — большую часть времени люди думают о «модулях» как о библиотеках DLL.

4. @Larry Под модулем я имел в виду DLL или EXE, так что то же самое, что и вы. Я, безусловно, не предлагаю вам указывать соглашение о вызовах при экспорте, но не при импорте. Очевидно, что эти два параметра должны совпадать.

5. Я хочу сказать, что даже в библиотеке должно быть объявлено и исправлено соглашение о вызовах. Единственный раз, когда небезопасно фиксировать соглашение о вызовах в stone, — это если вы компилируете автономную программу без библиотек.

Ответ №2:

Когда Windows переключилась с __cdecl на __stdcall в качестве соглашения о вызовах по умолчанию, размер продукта уменьшился примерно на 10%. Эта экономия была полностью связана с удалением настроек стека после вызова методов stdcall (__cdecl — это соглашение о вызове «вызывающий настраивает стек для удаления параметров», __stdcall — это соглашение о вызове «вызываемый настраивает стек для удаления параметров», поскольку вызывающих больше, чем вызываемых, переключение уменьшает размер ваших двоичных файлов).

Недостатком использования __stdcall является то, что у вас нет переменных # s аргументов (поскольку вызываемый объект настраивает стек, они не могут знать, сколько параметров указал вызывающий объект).

Итог: переключение на __stdcall из соглашения о вызовах по умолчанию может привести к уменьшению размера вашего двоичного файла. Это может быть или не быть важным для вас.

Однако, как упоминалось выше, если к вашему коду КОГДА-ЛИБО обращались в другой компиляции (например, если вы передаете файл .lib кому-то другому), абсолютно важно, чтобы вы объявили используемое соглашение о вызовах.

Ответ №3:

Единственная причина, по которой материал COM явно устанавливает соглашение о вызовах, заключается в том, что он используется через границы DLL.
Поэтому я бы посоветовал отказаться от явной настройки соглашения о вызовах и установить его в настройках компилятора.
В общем:
Если функции экспортируются как DLL, установите макрос, который определяет соглашение о вызовах в заголовках. Это не позволяет пользователям библиотеки DLL использовать неправильное соглашение о вызовах при подключении к вашей библиотеке DLL. Явный переопределяет настройку компилятора.
Не используйте конвекцию вызовов для локальных функций. Соглашение может быть установлено переключателем компилятора. Если вы решите установить его явно, сделайте это для всех функций. Тогда у вас все еще есть центральное место для изменения соглашения о вызовах.
Конечно, если это имеет смысл или вам нужно какое-то специальное соглашение о вызовах, например, fastcall для оптимизации, тогда вам также нужно установить явно.

Ответ №4:

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

Ответ №5:

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