c++ - usar - funciones anonimas c#
¿Por qué las funciones de subprocesos deben declararse como ''__cdecl''? (5)
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 de 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 de Pascal, las API de Win32 y MacOS utilizan de forma predeterminada esa convención de llamadas, excepto en ciertos casos.
Si esa función tiene solo un parámetro, en teoría el uso de 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 la convención de llamadas que utiliza un compilador en particular.
El código de muestra que muestra cómo crear subprocesos utilizando MFC declara que la función de subproceso es 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 ejemplo (MFC):
static __cdecl UINT MyFunc(LPVOID pParam)
{
...
}
CWinThread* pThread = AfxBeginThread(MyFunc, ...);
Considerando que Boost:
static void func()
{
...
}
boost::thread t;
t.create(&func);
(los ejemplos del código pueden no ser correctos al 100% ya que no estoy cerca de un IDE).
¿Cuál es el punto de __cdecl? ¿Cómo ayuda cuando se crean subprocesos?
Debido a que su hilo va a ser llamado por una función de tiempo de ejecución que lo gestiona, y esa función espera que sea de esa manera. 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.
Mira el prototipo de 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 a AfxBeginThread()
, debe coincidir con ese prototipo, incluida la convención de llamadas.
Las páginas de MSDN en __cdecl
y __stdcall
(así como __fastcall
y __thiscall
) explican los pros y los contras de cada convención de llamadas.
El boost::thread
constructor usa plantillas para permitirte pasar un puntero de función o un objeto de función invocable, por lo que no tiene las mismas restricciones que MFC.
__cdecl le dice al compilador que use la convención de llamadas C (a diferencia de stdcall, fastcall o cualquier otra convención de llamadas que admita su compilador). Creo que VC ++ usa stdcall por defecto.
La convención de llamadas afecta aspectos tales como cómo se insertan los argumentos en la pila (o registros, en el caso de la 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.
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 con una convención de llamada específica, que en este caso es una macro, WINAPI, que según mi sistema se define 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.
El motivo por el 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 ...