Почему функции потоков нужно объявлять как __cdecl?

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

Например (MFC):

static __cdecl UINT MyFunc(LPVOID pParam)
{
...
}

CWinThread* pThread = AfxBeginThread(MyFunc, ...);

В то время как Boost:

static void func()
{
...
}

boost::thread t;
t.create(&func);

(образцы кода могут быть не на 100% правильными, так как я далеко не IDE).

В чем смысл __cdecl? Как это помогает при создании потоков?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
6
0
3 392
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

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

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

Ответ принят как подходящий

__cdecl сообщает компилятору использовать соглашение о вызовах C (в отличие от stdcall, fastcall или любого другого соглашения о вызовах, которое поддерживает ваш компилятор). Я считаю, что VC++ по умолчанию использует stdcall.

Соглашение о вызовах влияет на такие вещи, как то, как аргументы помещаются в стек (или регистры, в случае fastcall) и кто извлекает аргументы из стека (вызывающий или вызываемый).

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

Boost не учитывает соглашение о вызовах. Это не функция языкового уровня (больше функция уровня компоновщика). MS использует его для обратной совместимости кода.

Martin York 04.10.2008 22:43

У Локи лучший ответ

SChalice 24.11.2015 03:08

Посмотрите на прототип AfxBeginThread():

CWinThread* AfxBeginThread(
   AFX_THREADPROC pfnThreadProc,
   LPVOID pParam,
   int nPriority = THREAD_PRIORITY_NORMAL,
   UINT nStackSize = 0,
   DWORD dwCreateFlags = 0,
   LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL 
);

AFX_THREADPROC - это typedef для UINT(AFX_CDECL*)(LPVOID). Когда вы передаете функцию в AfxBeginThread(), она должна соответствовать этому прототипу, включая соглашение о вызовах.

Страницы MSDN на __cdecl и __stdcall (а также __fastcall и __thiscall) объясняют плюсы и минусы каждого соглашения о вызовах.

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

Компиляторы C / C++ по умолчанию используют соглашение о вызовах C (сначала помещая самый правый параметр в стек), поскольку он позволяет работать с функциями с переменным номером аргумента как printf.

Соглашение о вызовах Паскаля (также известное как «fastcall») сначала устанавливает крайний левый параметр. Это быстрее, хотя и стоит вам возможности простых функций с переменными аргументами (я где-то читал, что они все еще возможны, хотя вам нужно использовать некоторые уловки).

Из-за скорости, обусловленной использованием соглашения Pascal, API Win32 и MacOS по умолчанию используют это соглашение о вызовах, за исключением некоторых случаев.

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

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

umm fastcall помещает первые 2 аргумента в ecx и edx. Затем помещает остальные в стек в том же порядке, что и остальные соглашения о вызовах. stdcall и cdecl отличаются тем, что stdcall очищает свой собственный стек, вызывающие cdecl должны очищать стек. Там есть thiscall который ecx == this

Raindog 22.10.2008 02:42

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

#define WINAPI      __stdcall

Это означает, что вызываемая функция отвечает за очистку стека. Причина, по которой boost :: thread может поддерживать произвольные функции, заключается в том, что он передает указатель на объект функции, используемый в вызове функции thread :: create в CreateThread. Threadproc, связанный с потоком, просто вызывает operator () для объекта функции.

Следовательно, причина, по которой MFC требует __cdecl, связана со способом внутреннего вызова функции, переданной в вызов AfxBeginThread. Нет веских причин для этого, если только они не планировали разрешить параметры vararg ...

Другие вопросы по теме