Punteros de función (typedef)

MQL5 tiene la palabra clave typedef , que le permite describir un tipo especial de puntero de función.

A diferencia de C ++, donde typedef tiene una aplicación mucho más amplia, en MQL5 typedef se utiliza sólo para los punteros de función.

La sintaxis para una nueva declaración de tipo es:

typedef function_result_type ( *function_type )( [list_of_input_parameters] ) ;

El identificador function_type define un nombre de tipo que se convierte en sinónimo (alias) de un puntero a cualquier función que devuelva un valor del tipo dado function_result_type y acepte una lista de parámetros de entrada (list_of_input_parameters).

Por ejemplo, podemos tener 2 funciones con los mismos prototipos (dos parámetros de entrada de tipo double y el tipo de resultado también es double) que realizan operaciones aritméticas diferentes: suma y resta (FuncTypedef.mq5).

double plus(double v1double v2)
{
   return v1 + v2;
}
 
double minus(double v1double v2)
{
   return v1 - v2;
}

Su prototipo común es fácil de describir para utilizarlo como puntero:

typedef double (*Calc)(doubledouble);

Esta entrada introduce en el programa el tipo Calc, con el que se puede definir una variable o parámetro para guardar o pasar una referencia a cualquier función con dicho prototipo, incluidas las funciones plus y minus. Este tipo es un puntero porque en la descripción se utiliza el carácter '*' (*Calc). Descubriremos más sobre las características del asterisco aplicadas a los punteros cuando estudiemos la programación orientada a objetos.

Es conveniente utilizar esta clase de punteros para crear algoritmos personalizados que puedan llamar «sobre la marcha» a diferentes funciones correspondientes al alias, dependiendo de los datos de entrada.

En concreto, podemos introducir una función calculadora generalizada:

double calculator(Calc ptrdouble v1double v2)
{
   if(ptr == NULLreturn 0;
   return ptr(v1v2);
}

Su primer parámetro se declara con el tipo Calc . Gracias a ello, podemos pasarle una función arbitraria con un prototipo adecuado y, como resultado, realizar alguna operación, cuya esencia desconoce la propia función calculator. Para ello, delega la llamada en un puntero: ptr(v1, v2). Dado que ptr es un puntero a una función, esta sintaxis no sólo se asemeja a una llamada a una función, sino que en realidad llama a la función que contiene el puntero.

Tenga en cuenta que comprobamos previamente el parámetro ptr con el valor especial NULL (NULL es el equivalente a cero para los punteros). El hecho es que el puntero puede no apuntar a ninguna parte, es decir, puede no estar inicializado. Así, en el script, tenemos una variable global descrita:

Calc calc;

No tiene punteros. Si no fuera por la «protección» contra NULL, llamar a calculator con un puntero «vacío» calc provocaría un error en tiempo de ejecución «Llamada a puntero de función no válida».

Las llamadas a la función calculator con diferentes punteros en el primer parámetro darán los siguientes resultados (mostrados en los comentarios):

void OnStart()
{
   Print(calculator(plus12));   //  3
   Print(calculator(minus12));  // -1
   Print(calculator(calc12));   //  0
}

Tenga en cuenta que si no hay inicialización explícita, todos los punteros de función se rellenan con valores cero. Esto se aplica tanto a las variables globales como a las locales de un tipo determinado.

Un tipo de puntero definido con typedef puede devolverse desde funciones, por ejemplo:

Calc generator(ushort type)
{
   switch(type)
   {
      case '+': return plus;
      case '-': return minus;
   }
   return NULL;
}

Además, el tipo de puntero de función se utiliza a menudo para funciones callback (callback, véase FuncCallback.mq5). Supongamos que tenemos una función DoMath que realiza cálculos largos (probablemente, está implementada en una biblioteca). En cuanto a comodidad y facilidad de uso de la interfaz, sería estupendo mostrar al usuario una indicación de progreso. Para ello, puede definir un tipo especial de puntero de función para notificaciones sobre el porcentaje de trabajo completado (ProgressCallback), y añadir un parámetro de este tipo a la función DoMath. En el código DoMath, debería llamar periódicamente a la función pasada:

typedef void (*ProgressCallback)(const float percent);
 
void DoMath(double &bigdata[], ProgressCallback callback)
{
   const int N = 1000000;
   for(int i = 0i < N; ++i)
   {
      if(i % 10000 == 0 && callback != NULL)
      {
         callback(i * 100.0f / N);
      }
      
      // long calculations
   }
}

A continuación, el código de llamada puede definir la función callback necesaria, pasarle un puntero a DoMath y recibir actualizaciones a medida que avanza el cálculo.

void MyCallback(const float percent)
{
   Print(percent);
}
  
void OnStart()
{
   double data[] = {0};
   DoMath(dataMyCallback);
}

Los punteros de función sólo funcionan con funciones personalizadas definidas en MQL5. No pueden señalar a funciones integradas de la API de MQL5.