Plantillas de funciones

Las funciones sobrecargadas suelen utilizarse para la ejecución de operaciones semejantes con diferentes tipos de datos. La función ArraySize() es un sencillo ejemplo en el lenguaje MQL5. Ella devuelve el tamaño del array de cualquier tipo. En realidad, esta función del sistema es una función sobrecargada, y toda la implementación de esta sobrecarga está ocultada de los desarrolladores de programas MQL5:

int  ArraySize(
   void&  array[]      // array a comprobar
   );

Es decir, en realidad para cada invocación de esta función el compilador del lenguaje MQL5 inserta una implementación necesaria. Por ejemplo, así es como se puede hacer para los arrays del tipo entero:

int  ArraySize(
   int&  array[]      // array con los elementos del tipo int
   );

Y para el array del tipo MqlRates que se usa para el trabajo con las cotizaciones en el formato de datos históricos, la función ArraySize() puede representarse de la siguiente manera:

int  ArraySize(
   MqlRates&  array[] // array llenado con los valores del tipo MqlRates
   );

De esta manera, resulta muy cómodo utilizar la misma función para trabajar con diferentes tipos. Pero hace falta realizar todo el trabajo preliminar de manera individual, es decir: sobrecargar la función necesaria para todos los tipos de datos con los que tendrá que trabajar correctamente.

Hay una solución más conveniente. Si las operaciones idénticas deben realizarse para cada uno de los tipos de datos, la solución más compacta y cómoda sería el uso de las plantillas de funciones. En este caso, al programador le hace falta escribir solamente una descripción de la plantilla de la función. Cuando la plantilla se describe de esta manera, será suficiente especificar algún parámetro formal en vez de un determinado tipo de datos con los que debe trabajar la función. El compilador va a generar automáticamente diferentes funciones para el procesamiento correspondiente de cada tipo basándose en los tipos de los argumentos utilizados durante la invocación de la función.

La definición de la plantilla de una función se empieza con la palabra clave template seguida de la lista de los parámetros formales encerrados entre los corchetes angulares. Antes de cada parámetro formal se pone la palabra clave typename. Los tipos formales de los parámetros son los tipos built-in o los tipos definidos por el usuario. Se utilizan:

  • para especificar los tipos de argumentos de la función,
  • para especificar los tipos del valor devuelto de la función,
  • para declarar variables dentro del cuerpo de descripción de la función.

 

El número de parámetros de una plantilla no podrá exceder de ocho. Cada parámetro formal de la definición de la plantilla tiene que aparecer en la lista de los parámetros de la función por lo menos una vez. Cada uno de los nombres del parámetro formal tiene que ser único.

Aquí tenemos un ejemplo de una función para la búsqueda del valor máximo en el array de cualquier tipo numérico (números enteros y reales):

template<typename T>
T ArrayMax(T &arr[])
  {
   uint size=ArraySize(arr);
   if(size==0) return(0);          
   
   T max=arr[0];
   for(uint n=1;n<size;n++)
      if(max<arr[n]) max=arr[n];
//---
   return(max);
  }

Esta plantilla define la función que busca el valor máximo en el array pasado y devuelve este valor como resultado. Vamos a recordar que la función ArrayMaximum() incorporada en MQL5 devuelve sólo el índice del valor máximo encontrado que será utilizado a continuación para obtener el mismo valor en cuestión. Por ejemplo:

//--- creamos un array
   double array[];
   int size=50;
   ArrayResize(array,size);
//---  lo llenamos con valores aleatorios
   for(int i=0;i<size;i++)
     {
      array[i]=MathRand();
     }
 
//--- buscamos la posición del elemento máximo en el array
   int max_position=ArrayMaximum(array);
//--- ahora obtenemos el mismo valor máximo en el array
   double max=array[max_position];
//--- visualizamos el valor encontrado
   Print("Max value = ",max);

De esta manera, hemos necesitado dos pasos para obtener el valor máximo del array. A través de la plantilla de la función ArrayMax() podemos obtener el resultado del tipo necesario con sólo pasar el array del tipo correspondiente a esta función. Es decir, en vez de las últimas dos líneas

//--- buscamos la posición del elemento máximo en el array
   int max_position=ArrayMaximum(array);
//--- ahora obtenemos el mismo valor máximo en el array
   double max=array[max_position];

ahora podemos usar una sola línea que va a devolver de una sola vez el resultado del mismo tipo que tiene el array pasado:

//--- buscamos el valor máximo
   double max=ArrayMax(array);

En este caso el tipo del resultado devuelto por la función ArrayMax() va a corresponder automáticamente al tipo del array.

 

Es necesario usar la palabra clave typename para obtener el tipo del argumento en forma de una cadena, con el fin de crear modos universales de trabajo con diferentes tipos de datos. Vamos a demostrarlo con el ejemplo de una función que devuelve el tipo de datos en forma de una cadena:

#include <Trade\Trade.mqh>
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- 
   CTrade trade;   
   double d_value=M_PI;
   int i_value=INT_MAX;
   Print("d_value: type=",GetTypeName(d_value), ",   value=", d_value);
   Print("i_value: type=",GetTypeName(i_value), ",   value=", i_value);
   Print("trade: type=",GetTypeName(trade));
//--- 
  }
//+------------------------------------------------------------------+
//| El tipo se devuelve como una línea                                  |
//+------------------------------------------------------------------+
template<typename T>
string GetTypeName(const T &t)
  {
//--- devolvemos el tipo en forma de una línea
   return(typename(T));
//---
  }

 

Las plantillas de las funciones también se puede utilizar para los métodos de clase, por ejemplo:

class CFile
  {
   ...
public:
   ...
   template<typename T>
   uint WriteStruct(T &data);
  };
 
template<typename T>
uint CFile::WriteStruct(T &data)
  {
   ...
   return(FileWriteStruct(m_handle,data));
  }

Las plantillas de las funciones no se puede declarar con las palabras clave export, virtual y #import.

Sobrecarga de las funciones de plantilla

En algunos casos, podría ser necesaria la sobrecarga de la función de plantilla. Por ejemplo, tenemos una función de plantilla que registra en el primer parámetro el valor del segundo parámetro  con la ayuda de la conversión explícita de tipos. En el lenguaje MQL5 está prohibida la conversión del tipo string al tipo bool, pero podemos hacerla por nosotros mismos, para ello, creamos la sobrecarga de la función de plantilla. Por ejemplo:

//+------------------------------------------------------------------+
//| Función de plantilla                                                |
//+------------------------------------------------------------------+
template<typename T1,typename T2>
string Assign(T1 &var1,T2 var2)
  {
   var1=(T1)var2;
   return(__FUNCSIG__);
  }
//+------------------------------------------------------------------+
//| Sobrecarga especial para el caso bool+string                    |
//+------------------------------------------------------------------+
string Assign(bool &var1,string var2)
  {
   var1=(StringCompare(var2,"true",false) || StringToInteger(var2)!=0);
   return(__FUNCSIG__);
  }
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   int i;
   bool b;
   Print(Assign(i,"test"));
   Print(Assign(b,"test"));
  }

Como resultado de la ejecución de este código, veremos que para la pareja int+string  se ha usado la función de plantilla Assign(), y en la segunda llamada bool+string ya se ha usado la función sobrecargada.

string Assign<int,string>(int&,string)
string Assign(bool&,string)

Vea también

Sobrecarga