Почему функции потока должны быть объявлены как '__cdecl'? - PullRequest
6 голосов
/ 04 октября 2008

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

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

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

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

Принимая во внимание:

static void func()
{
...
}

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

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

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

Ответы [ 5 ]

4 голосов
/ 04 октября 2008

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

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

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

4 голосов
/ 04 октября 2008

Посмотрите на прототип для 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.

1 голос
/ 18 октября 2008

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

#define WINAPI      __stdcall

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

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

1 голос
/ 04 октября 2008

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

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

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

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

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

1 голос
/ 04 октября 2008

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...