2008-10-04 17 views
6

El código de muestra que muestra cómo crear subprocesos utilizando MFC declara la función de subproceso como estática y __cdecl. ¿Por qué se requiere esto último? Los hilos Boost no se molestan con esta convención, ¿entonces es solo un anacronismo?¿Por qué las funciones de subprocesos deben declararse como '__cdecl'?

Por ejemplo (MFC):

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

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

Mientras Boost:

static void func() 
{ 
... 
} 

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

(los ejemplos de código pueden no ser 100% correcto ya que estoy muy lejos de un IDE).

¿Cuál es el punto de __cdecl? ¿Cómo ayuda cuando se crean subprocesos?

Respuesta

4

__cdecl le dice al compilador que use la convención de llamadas C (a diferencia de la llamada stdcall, de llamada rápida o cualquier otra convención de llamadas que admita su compilador). Creo que VC++ usa stdcall por defecto.

La convención de llamadas afecta a cosas tales como cómo se envían los argumentos a la pila (o registros, en el caso de una llamada rápida) y quién saca los argumentos de la pila (llamante o destinatario).

En el caso de Boost. Creo que utiliza la especialización de plantillas para descubrir el tipo de función y la convención de llamadas apropiadas.

+0

Boost no considera convención de llamadas. Esta no es una función de nivel de idioma (más una función de nivel de Enlazador). MS lo usa para compatibilidad con versiones anteriores del código. –

+0

Loki tiene la mejor respuesta – SChalice

1

Porque una función de tiempo de ejecución llamará a su subproceso que lo gestione, y esa función espera que sea así. Boost lo diseñó de una manera diferente.

Ponga un punto de interrupción al comienzo de su función de subproceso y mire la pila cuando se llame, verá la función de tiempo de ejecución que lo llama.

4

Mira el prototipo para 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 es un typedef para UINT(AFX_CDECL*)(LPVOID). Cuando pasa una función al AfxBeginThread(), debe coincidir con ese prototipo, incluida la convención de llamadas.

Las páginas de MSDN sobre __cdecl y __stdcall (así como __fastcall y __thiscall) explicar los pros y los contras de cada convención de llamada.

El constructor boost::thread utiliza plantillas que le permiten pasar un puntero de función o un objeto de función invocable, por lo que no tiene las mismas restricciones que MFC.

1

Los compiladores C/C++ utilizan de forma predeterminada la convención de llamadas C (presionando param derecho para más adelante en la pila) porque permite trabajar con funciones con número de argumento variable como printf.

La convención de llamadas Pascal (también conocida como "llamada rápida") empuja primero al param más a la izquierda. Esto es más rápido, aunque le cuesta la posibilidad de funciones de argumentos variables fáciles (leo en algún lado que todavía son posibles, aunque necesita usar algunos trucos).

Debido a la velocidad resultante del uso de la convención Pascal, ambas API Win32 y MacOS utilizan de manera predeterminada esa convención de llamadas, excepto en ciertos casos.

Si esa función tiene solo un parámetro, en teoría usar cualquiera de las convenciones de llamadas sería legal, aunque el compilador puede exigir que se use la misma convención de llamadas para evitar cualquier problema.

Las bibliotecas de impulso se diseñaron teniendo en cuenta la portabilidad, por lo que deberían ser independientes de qué convención de llamadas está utilizando un compilador en particular.

+0

umm fastcall pone los primeros 2 argumentos en ecx y edx. A continuación, coloca el resto en la pila en el mismo orden que el resto de las convenciones de llamada. stdcall y cdecl difieren en que stdcall limpia su propia pila, los llamadores cdecl tienen que limpiar la pila. Hay una llamada que ecx == esto – Raindog

1

La respuesta real tiene que ver con la forma en que Windows llama internamente a la rutina de procuración de subprocesos, y espera que la función cumpla una convención de llamada específica, que en este caso es una macro, WINAPI, que según mi sistema definido como:

#define WINAPI  __stdcall 

Esto significa que la función llamada es responsable de limpiar la pila. La razón por la cual boost :: thread es capaz de soportar funciones arbitrarias es que pasa un puntero al objeto de función usado en la llamada a la función thread :: create para CreateThread. El subproceso asociado con el subproceso simplemente llama a operator() en el objeto de función.

La razón por la que MFC requiere __cdecl tiene que ver con la forma en que internamente llama a la función transferida a la llamada a AfxBeginThread. No hay una buena razón para hacer esto a menos que estén planeando permitir parámetros vararg ...

Cuestiones relacionadas