Foros del Web » Programación para mayores de 30 ;) » C/C++ »

[SOLUCIONADO] Escrudiñando el api de C++

Estas en el tema de Escrudiñando el api de C++ en el foro de C/C++ en Foros del Web. Buenas noches, colegas del foro. Estuve haciendo una aplicacion en java y me adentrè a su api. Alli me encontre que podia serme util un ...
  #1 (permalink)  
Antiguo 27/09/2015, 17:52
 
Fecha de Ingreso: mayo-2013
Ubicación: bogota
Mensajes: 157
Antigüedad: 10 años, 10 meses
Puntos: 2
Escrudiñando el api de C++

Buenas noches, colegas del foro.
Estuve haciendo una aplicacion en java y me adentrè a su api.
Alli me encontre que podia serme util un JList<>. Me nacio la duda de saber para que sirven los operadores "<>" y me di cuenta que el funcionamiento de estos provienen del c++.

Investigando acerca de esto en el api de C++, me acabo de estrellar con lo siguente.

Código C++:
Ver original
  1. typedef typename _Ht::key_type key_type;

pero por mas que busco y analiso no encuentro respuesta.
Sè que typedef =definir un tipo de dato.
typename=esto se utiliza dentro de una plantilla, y de esta manera no se para que sirve.
::se utilizan para accesar a algun miembro de su respectiva clase.

Se supone que lo que està a la derecha de los puntos es un metodo pero esto no tiene cara de ser un metodo.


¿alguien podria ayudarme por favor?.
  #2 (permalink)  
Antiguo 28/09/2015, 03:21
 
Fecha de Ingreso: octubre-2014
Ubicación: Madrid
Mensajes: 1.212
Antigüedad: 9 años, 6 meses
Puntos: 204
Respuesta: Escrudiñando el api de C++

typedef sirve para definir un alias:

Código C++:
Ver original
  1. typedef unsigned int miUint;
  2. miUint dato; // el tipo real de dato es unsigned int

A partir del estándar C++11 también se puede usar using:

Código C++:
Ver original
  1. using miUint = unsigned int;
  2. miUint dato; // el tipo real de dato es unsigned int

por otro lado, lo que se está intentando en esa línea es asignar un alias basándose en un tipo que proviene de un template.

Para explicarlo voy a usar un ejemplo tonto ad-hoc.

imagínate que tienes una colección de clases que, entre otras cosas, cada una te proporciona un tipo de dato de la siguiente forma:

Código C++:
Ver original
  1. struct dummy1
  2. {
  3.   typedef char tipoConcreto;
  4. };
  5.  
  6. struct dummy2
  7. {
  8.   typedef int tipoConcreto;
  9. };

Ahora imagina que necesitas crear un template que ha de tener una variable cuyo tipo ha de coincidir con el de un tipoConcreto en particular. Un primer acercamiento podría ser:

Código C++:
Ver original
  1. template<typename T>
  2. struct TypeTemplate
  3. {
  4.   typedef T::tipoConcreto t;
  5.  
  6.   t variable;
  7. };

pero claro, si intentamos usar este template directamente en nuestro código nos encontraremos con un bonito error de compilación. El problema es que T::tipoConcreto no es un nombre cualificado, es dependiente de la especialización que usemos. En estos casos necesitamos usar typename. Tómalo como una protección que te exige el compilador para asegurarse de que sabes lo que estás haciendo.

Código C++:
Ver original
  1. template<typename T>
  2. struct TypeTemplate
  3. {
  4.   typedef typename T::tipoConcreto t;
  5.  
  6.   t variable;
  7. };

Ahora ya no da errores de compilación y podemos probar nuestro código:

Código C++:
Ver original
  1. int main()
  2. {
  3.   TypeTemplate<dummy1> miTemplate1;
  4.   TypeTemplate<dummy2> miTemplate2;
  5.   miTemplate1.variable = 60;
  6.   miTemplate2.variable = 60;
  7.  
  8.   std::cout << miTemplate1.variable << " " << miTemplate2.variable;
  9. }

La salida del programa será:

Código :
Ver original
  1. < 60

En primer lugar ha imprimido un carácter, puesto que dummy1::tipoConcreto es de tipo char. Concretamente ha imprimido el carácter 60. A continuación imprime un entero, ya que dummy2::tipoConcreto es de tipo int.

Espero que con este ejemplo haya quedado resuelta la duda.

Un saludo.
  #3 (permalink)  
Antiguo 29/09/2015, 07:38
 
Fecha de Ingreso: mayo-2013
Ubicación: bogota
Mensajes: 157
Antigüedad: 10 años, 10 meses
Puntos: 2
Respuesta: Escrudiñando el api de C++

Compañero todo muy claro.
Agradezco tu ayuda y pido que por favor disculpes mi ignorancia.

Gracias.
  #4 (permalink)  
Antiguo 29/09/2015, 07:42
 
Fecha de Ingreso: octubre-2014
Ubicación: Madrid
Mensajes: 1.212
Antigüedad: 9 años, 6 meses
Puntos: 204
Respuesta: Escrudiñando el api de C++

Cita:
Iniciado por dacroma Ver Mensaje
Compañero todo muy claro.
Agradezco tu ayuda y pido que por favor disculpes mi ignorancia.

Gracias.
Dominar los templates no es nada fácil... tienen muchos detalles y su sintaxis no es un prodigio de la claridad precisamente.

Yo muchas veces me encuentro con templates a los que les tengo que dedicar varias horas para entender su funcionamiento... ahora, con templates se pueden hacer auténticas maravillas.

No es culpa de tu ignorancia, simplemente es que son complejos.

Un saludo
  #5 (permalink)  
Antiguo 29/09/2015, 13:26
 
Fecha de Ingreso: febrero-2015
Mensajes: 404
Antigüedad: 9 años, 1 mes
Puntos: 3
Respuesta: Escrudiñando el api de C++

La verdad es que de templates yo no tengo ni idea y lo que pones se ve muy interesante. No acabo de entender el código ¿podrías poner todo el código necesario de tu ejemplo junto y dejar fuera lo que sobra? Así lo veré más claro.
Por otro lado ¿con templates se puede hacer que una función tenga diferentes tipos de retorno o que las variables que se usen de parámetros sean de diferentes tipos? Creo que lo de los parámetros es fácil con sobrecarga pero lo del retorno ni idea.
  #6 (permalink)  
Antiguo 30/09/2015, 01:54
 
Fecha de Ingreso: septiembre-2010
Mensajes: 494
Antigüedad: 13 años, 6 meses
Puntos: 10
Respuesta: Escrudiñando el api de C++

Hola aguml:

Puedes hacer plantillas de funciones y plantillas de estructuras/clases.
Y si, puedes parametrizar también el tipo de retorno:
Ejemplo de una función con plantilla:
Código C++:
Ver original
  1. template <typename T>
  2. T Duplicar (T valor)
  3. {
  4.     return 2*valor;
  5. }

Y la llamada:
Código C++:
Ver original
  1. int n= 3;
  2.     cout<<Duplicar(n)<<endl;
  3.     float f=4.0298;
  4.     cout<<Duplicar(f)<<endl;

Mejor explicado está aquí:
http://c.conclase.net/curso/?cap=040

Es preferible entender primero las plantillas "básicas" y luego entender la mezcla de plantilla con redefinición de tipos como en el ejemplo de @eferion
__________________
Mi calculadora en Qt
  #7 (permalink)  
Antiguo 30/09/2015, 03:35
 
Fecha de Ingreso: octubre-2014
Ubicación: Madrid
Mensajes: 1.212
Antigüedad: 9 años, 6 meses
Puntos: 204
Respuesta: Escrudiñando el api de C++

Los templates son algo tan sumamente complejo que en las universidades, si se da, es casi de refilón.

Es una herramienta muy potente... el problema es que no distingue entre amigos y enemigos y es bastante sencillo pegarse un tiro en el pie.

Una de las grandes virtudes de los teplates es que permiten realizar ciertos cálculos en tiempo de compilación, lo cual puede mejorar el rendimiento de la aplicación.

Por ejemplo, el clásico ejemplo de cálculo de factoriales. En primer lugar tenemos una función normal para calcular el factorial:

Código C++:
Ver original
  1. int factorial(int n)
  2. {
  3.   int toReturn=1;
  4.  
  5.   if(n>1)
  6.   {
  7.     do
  8.       toReturn *= n;
  9.     while(--n);
  10.   }
  11.  
  12.   return toReturn;
  13. }

Ahora la versión con templates:

Código C++:
Ver original
  1. template<int T>
  2. struct Factorial
  3. {
  4.   enum { valor=T*Factorial<T-1>::valor };
  5. };
  6.  
  7. template<>
  8. struct Factorial<0>
  9. {
  10.     enum { valor=1 };
  11. };

La especialización Factorial<0> sirve para que el template normal termine las iteraciones en un momento dado y no empiece a multiplicar números negativos.

Y ahora vamos a poner un pequeño código que compare el tiempo de ejecución de ambas versiones:

Código C++:
Ver original
  1. const long MAX = 100000000;
  2. int main(int argc, char *argv[])
  3. {
  4.   QTime t;
  5.   t.start();
  6.   int acumulador = 0;
  7.   for( auto i=0L;i<MAX;i++)
  8.     acumulador += Factorial<10>::valor;
  9.  
  10.   std::cout << "Template: " << t.elapsed() << " ms"<< std::endl;
  11.  
  12.   t.start();
  13.   for( auto i=0L;i<MAX;i++)
  14.     acumulador += factorial(10);
  15.  
  16.   std::cout << "Funcion: " << t.elapsed() << " ms" << std::endl;
  17.  
  18.   // Esta línea la pongo para evitar que el compilador pueda hacer optimizaciones
  19.   // que eliminen el código de los bucles.
  20.   std::cout << acumulador << std::endl;
  21. }

Por comodidad y por hacer el ejemplo rápido he usado QTimer que es una clase específica de Qt, pero podéis sustituirla por el control de tiempos que os de la gana.

Bueno, ya tenemos todo listo. Arrancamos en modo release y la salida por pantalla es la siguiente:

Código :
Ver original
  1. Template: 0 ms
  2. Funcion: 753 ms

¿Por qué tanta diferencia? Al compilarse el template el compilador tiene que calcular el valor concreto del enum, así que no le queda más remedio que obtener el valor de Factorial<10> durante la compilación. Una vez que el programa entra en ejecución, como el valor ya ha sido calculado, recuperarlo no cuesta absolutamente nada de tiempo, mientras que la opción clásica se ve obligada a calcular el valor cada vez que se lo pidan.

Mejoras de C++11

A partir del estándar de C++11 se puede usar el modificador constexpr en determinadas funciones. Este modificador viene a decir que ante una misma entrada la función SIEMPRE devolverá la misma salida. Esta característica permite que el compilador realice ciertas optimizaciones en beneficio del rendimiento. Eso sí, para poder usar este modificador hay que cumplir una serie de restricciones que no viene a cuento enumerarlas ahora mismo.

En este caso la función podría quedar así:

Código C++:
Ver original
  1. constexpr int factorial(int n)
  2. {
  3.   return (n>1)? n*factorial(n-1) : 1;
  4. }

Ejecutamos de nuevo el código y ahora los tiempos cambian sensiblemente:

Código :
Ver original
  1. Template: 0 ms
  2. Funcion: 595 ms

La función ha mejorado casi un 20% su rendimiento, pero aún así queda bastante lejos de la solución con templates.

Y esto es solo la punta del iceberg, con templates podemos diseñar lo que se nos ocurra. Algunos ejemplos:
  • Realizar conversiones de un tipo de dato cualquiera en otro usando únicamente una función: int valor = Convertir<int>("123456");
  • Contenedores para almacenar colecciones de elementos: como ejemplo todos los de la STL
  • ...

Eso sí, insisto. Dominar el tema de los templates no es sencillo y no hay demasiada gente que sea capaz de leerlos con soltura. Si alguien tiene ganas de coger un dolor de cabeza mientras explora la potencia de los templates puede echar un vistazo al libro: Modern C++ Design, de Andrei Alexandrescu.

Estáis avisados.

Un saludo.
  #8 (permalink)  
Antiguo 30/09/2015, 09:18
 
Fecha de Ingreso: febrero-2015
Mensajes: 404
Antigüedad: 9 años, 1 mes
Puntos: 3
Respuesta: Escrudiñando el api de C++

Cita:
Iniciado por eferion Ver Mensaje
Si alguien tiene ganas de coger un dolor de cabeza mientras explora la potencia de los templates puede echar un vistazo al libro: Modern C++ Design, de Andrei Alexandrescu.

Estáis avisados.

Un saludo.
Jajaja. ¿conocéis de algún escrito en el que se trate lo más básico y así poder empezar con buen paso? Llevo toda la semana con un resfriado y con dolor de cabeza y no quiero que se haga crónico por culpa de los templates así que quiero ver primero la teoría más básica.
  #9 (permalink)  
Antiguo 30/09/2015, 09:19
 
Fecha de Ingreso: octubre-2014
Ubicación: Madrid
Mensajes: 1.212
Antigüedad: 9 años, 6 meses
Puntos: 204
Respuesta: Escrudiñando el api de C++

Que tal "C++ Templates The Complete Guide" de David Vandevoorde y Nicolay M. Josuttis (espero haber escrito bien los nombres)
  #10 (permalink)  
Antiguo 01/10/2015, 02:23
 
Fecha de Ingreso: febrero-2015
Mensajes: 404
Antigüedad: 9 años, 1 mes
Puntos: 3
Respuesta: Escrudiñando el api de C++

Gracias amigos, voy a intentar encontrarlo en español porque en ingles estoy pegado y si tengo que andar traduciendo me puedo pegar un siglo y voy a acabar loco jejeje.
  #11 (permalink)  
Antiguo 01/10/2015, 10:15
 
Fecha de Ingreso: febrero-2015
Mensajes: 404
Antigüedad: 9 años, 1 mes
Puntos: 3
Respuesta: Escrudiñando el api de C++

He estado viendo un poquito sobre templates y he visto como crear una plantilla de función y hay algo que no entiendo el porque debe ser asi. Por ejemplo, si creo esto:
Código C++:
Ver original
  1. template <class T>
  2. T funcion (int a, int b)
Daría un error al compilar y según entendí es porque el tipo del template tiene que ir en los parámetros al menos una vez. ¿por qué de esa restricción? ¿alguien lo sabe?
En la página 4 del siguiente pdf dice lo del error pero no explica del porque lo hicieron asi:

https://www.google.es/url?sa=t&sourc...4OX9EbkBEwxDSQ
  #12 (permalink)  
Antiguo 01/10/2015, 10:26
 
Fecha de Ingreso: octubre-2014
Ubicación: Madrid
Mensajes: 1.212
Antigüedad: 9 años, 6 meses
Puntos: 204
Respuesta: Escrudiñando el api de C++

No es porque sea un template... en C++ no puedes tener dos funciones que difieran únicamente en el tipo de dato de retorno:

Código C++:
Ver original
  1. // Esto no compila
  2. int func(char)
  3. {return 0; }
  4.  
  5. char func(int)
  6. { return '0'; }

Para hacer eso hay que recurrir al truco de usar la sobrecarga del operador función de una clase, pero eso ya lo verás más adelante que no es plan de agobiarte a conceptos jejeje.
  #13 (permalink)  
Antiguo 01/10/2015, 11:20
 
Fecha de Ingreso: febrero-2015
Mensajes: 404
Antigüedad: 9 años, 1 mes
Puntos: 3
Respuesta: Escrudiñando el api de C++

Ok gracias por la aclaración.

Etiquetas: api, funcion
Atención: Estás leyendo un tema que no tiene actividad desde hace más de 6 MESES, te recomendamos abrir un Nuevo tema en lugar de responder al actual.
Respuesta

SíEste tema le ha gustado a 1 personas




La zona horaria es GMT -6. Ahora son las 20:23.