为什么线程函数需要声明为'__cdecl'?

时间:2008-10-04 14:05:20

标签: c++ multithreading mfc boost

显示如何使用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 个答案:

答案 0 :(得分:4)

__ cdecl告诉编译器使用C调用约定(而不是stdcall,fastcall或编译器支持的任何其他调用约定)。我相信,VC ++默认使用stdcall。

调用约定会影响诸如如何将参数压入堆栈(或者在fastcall的情况下为寄存器)以及谁从堆栈(调用者或被调用者)中弹出参数。

在Boost的情况下。我相信它使用模板特化来确定适当的函数类型和调用约定。

答案 1 :(得分:4)

查看AfxBeginThread()的原型:

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

AFX_THREADPROCUINT(AFX_CDECL*)(LPVOID)的typedef。将函数传递给AfxBeginThread()时,它必须与该原型匹配,包括调用约定。

__cdecl__stdcall上的MSDN页面(以及__fastcall__thiscall)解释了每个调用约定的优缺点。

boost::thread构造函数使用模板允许您传递函数指针或可调用函数对象,因此它没有与MFC相同的限制。

答案 2 :(得分:1)

因为你的线程将由为你管理它的运行时函数调用,并且该函数期望它是这样的。 Boost以不同的方式设计它。

在线程函数的开头放置一个断点,并在调用它时查看堆栈,你会看到调用你的运行时函数。

答案 3 :(得分:1)

C / C ++编译器默认使用C调用约定(首先在堆栈上推送最右边的参数),因为它允许使用带有可变参数编号的函数作为printf。

Pascal调用约定(又名“fastcall”)首先推动最左边的param。这会更快,但是你可以使用简单的变量参数函数(尽管你需要使用一些技巧,但我仍然可以阅读它们。)

由于使用Pascal约定产生的速度,默认情况下Win32和MacOS API都使用该调用约定,但在某些情况下除外。

如果该函数只有一个参数,理论上使用任何一个调用约定都是合法的,尽管编译器可以强制执行相同的调用约定来避免任何问题。

boost库的设计注重可移植性,因此他们应该不知道特定编译器使用的调用者约定。

答案 4 :(得分:1)

真正的答案与windows内部如何调用线程proc例程有关,并且它期望函数遵守特定的调用约定,在本例中是一个宏,WINAPI,根据我的系统定义为:

#define WINAPI      __stdcall

这意味着被调用的函数负责清理堆栈。 boost :: thread能够支持任意函数的原因是它将指向调用thread :: create function的函数对象的指针传递给CreateThread。与线程关联的threadproc只是在函数对象上调用operator()。

因此,MFC需要__cdecl的原因与内部调用传递给AfxBeginThread调用的函数的方式有关。除非他们计划允许使用vararg参数,否则没有充分的理由这样做......