Пример кода, показывающий, как создавать потоки с помощью 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? Как это помогает при создании потоков?





Потому что ваш поток будет вызываться функцией времени выполнения, которая управляет этим за вас, и эта функция ожидает, что это будет именно так. Boost спроектировал это иначе.
Поместите точку останова в начало функции потока и посмотрите на стек, когда он будет вызван, вы увидите функцию времени выполнения, которая вас вызывает.
__cdecl сообщает компилятору использовать соглашение о вызовах C (в отличие от stdcall, fastcall или любого другого соглашения о вызовах, которое поддерживает ваш компилятор). Я считаю, что VC++ по умолчанию использует stdcall.
Соглашение о вызовах влияет на такие вещи, как то, как аргументы помещаются в стек (или регистры, в случае fastcall) и кто извлекает аргументы из стека (вызывающий или вызываемый).
В случае с Boost. Я считаю, что он использует специализацию шаблонов для определения подходящего типа функции и соглашения о вызовах.
У Локи лучший ответ
Посмотрите на прототип 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
Настоящий ответ связан с тем, как окна внутренне вызывают подпрограмму процесса потока, и ожидается, что функция будет соответствовать определенному соглашению о вызовах, которое в данном случае является макросом WINAPI, который, согласно моей системе, определяется как:
#define WINAPI __stdcall
Это означает, что вызываемая функция отвечает за очистку стека. Причина, по которой boost :: thread может поддерживать произвольные функции, заключается в том, что он передает указатель на объект функции, используемый в вызове функции thread :: create в CreateThread. Threadproc, связанный с потоком, просто вызывает operator () для объекта функции.
Следовательно, причина, по которой MFC требует __cdecl, связана со способом внутреннего вызова функции, переданной в вызов AfxBeginThread. Нет веских причин для этого, если только они не планировали разрешить параметры vararg ...
Boost не учитывает соглашение о вызовах. Это не функция языкового уровня (больше функция уровня компоновщика). MS использует его для обратной совместимости кода.