2008-10-04 74 views
6

演示如何使用MFC创建线程的示例代码将线程函数声明为static和__cdecl。为什么需要后者?提升线程不会打扰这个约定,那么它只是一个时代错误?为什么线程函数需要声明为'__cdecl'?

例如(MFC):

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

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

而提升:

static void func() 
{ 
... 
} 

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

(因为我无处一个IDE附近的代码样本可能不是100%正确的)。

__cdecl有什么意义?创建线程时它有什么帮助?

回答

4

__cdecl告诉编译器使用C调用约定(与stdcall,fastcall或其他编译器支持的调用约定相反)。我相信,VC++默认使用stdcall。

调用约定会影响诸如参数如何被压入栈(或者寄存器,如果是fastcall)以及谁从堆栈(调用者或被调用者)弹出参数。

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

+0

Boost不考虑调用约定。这不是语言级别的功能(更多是链接器级别功能)。 MS使用它来代码的向后兼容性。 – 2008-10-04 18:43:34

+0

Loki有最佳答案 – SChalice 2015-11-24 00:08:45

1

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

在线程函数的开始处放置一个断点,并在调用堆栈时查看堆栈,您将看到调用您的运行时函数。

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(以及__fastcall__thiscall)的MSDN页面解释了每个调用约定的优缺点。

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

1

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

帕斯卡调用约定(又名“fastcall”)首先推动最左边的参数。尽管你需要使用一些技巧,但是这会让你更容易使用可变参数函数(我读过它们仍然有可能的地方)。

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

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

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

1

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

#define WINAPI  __stdcall 

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

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

相关问题