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

Crear plantilla dynamic. Lo que viene a ser tuple para struct, hacerlo para union.

Estas en el tema de Crear plantilla dynamic. Lo que viene a ser tuple para struct, hacerlo para union. en el foro de C/C++ en Foros del Web. Buenas, Leyendo el hilo sobre Crear una mascara... . Vi la implementación de eferion de la clase Variant y se me ocurrió crear una clase ...
  #1 (permalink)  
Antiguo 30/11/2014, 05:11
 
Fecha de Ingreso: julio-2006
Ubicación: Barcelona
Mensajes: 244
Antigüedad: 17 años, 9 meses
Puntos: 32
Crear plantilla dynamic. Lo que viene a ser tuple para struct, hacerlo para union.

Buenas,

Leyendo el hilo sobre Crear una mascara.... Vi la implementación de eferion de la clase Variant y se me ocurrió crear una clase con variadic templates que hiciera lo mismo que la clase std::tuple de C++, pero en este caso, usando como base para la implementación union en vez de struct. Puediendo así crear una unión de tipos distintos así:

Código C++:
Ver original
  1. dynamic<int, float, char> a;
  2. dynamic<int, std::string> b;

El problema es que yo nunca he profundizado en el tema de variadic templates, y me cuesta hacer la implementación. A ver si con la implementación de este ejemplo acabo aclarando algunas cosas sobre este tema. Me he bloquedado con lo siguiente:

Código C++:
Ver original
  1. #include <iostream>
  2.  
  3. template <typename... Types>
  4. union dynamic; // undefined
  5.  
  6. template <>
  7. union dynamic<> { }; // empty tuple
  8.  
  9. template <typename Head, typename... Tail>
  10. union dynamic<Head, Tail...> {
  11.     typedef Head type;
  12.  
  13.     Head first_elem;
  14.     dynamic<Tail...> rest;
  15. };
  16.  
  17. // definición de dynamic_element para conseguir el tipo de retorno de la función `get`
  18. template<size_t, typename> struct dynamic_element;
  19.  
  20. template<typename T, typename... Ts>
  21. struct dynamic_element<0, dynamic<T, Ts...>>
  22. {
  23.     typedef T type;
  24. };
  25.  
  26. template <size_t N, typename T, typename... Ts>
  27. struct dynamic_element<N, dynamic<T, Ts...>>
  28. {
  29.     typedef typename dynamic_element<N-1,dynamic<Ts...>>::type type;
  30. };
  31.  
  32. // definición de la función `get` que obtiene el n-simo miembro de la union
  33. template <typename T, typename... Ts>
  34. typename dynamic_element<0, dynamic<T, Ts...> >::type& get(dynamic<T, Ts...>& tpl) noexcept {
  35.     return tpl.first_elem;
  36. }
  37.  
  38. template <size_t N, typename T, typename... Ts>
  39. typename dynamic_element< N, dynamic<T, Ts...> >::type& get(dynamic<T, Ts...>& tpl) noexcept {
  40.     return get<N-1>(tpl.rest);
  41. }
  42.  
  43. int main() {
  44.     dynamic<int,char> test;
  45.     auto& num = get<0>(test);
  46.     //num = 5;
  47.     return 0;
  48. }

La implementación de dynamic y dynamic_element las he implementado casi calcadas de ejemplos que he encontrado por internet al ver que mis intentos de implementarlas eran infructuosos. La función get, la he implementado intentando seguir el mismo patrón, pero claro, con la diferencia de que estoy implementando una función. Pero algo he hecho mal.

A ver si algún experto me podría ayudar a entender que hago mal.

Un saludo y gracias!
__________________
github.com/xgbuils | npm/xgbuils
  #2 (permalink)  
Antiguo 30/11/2014, 16:24
 
Fecha de Ingreso: octubre-2014
Ubicación: Madrid
Mensajes: 1.212
Antigüedad: 9 años, 6 meses
Puntos: 204
Respuesta: Crear plantilla dynamic. Lo que viene a ser tuple para struct, hacerlo par

Le echaré un vistazo pero no prometo nada... Aún no he tenido que usar variadic templates en ninguna situación :)
  #3 (permalink)  
Antiguo 30/11/2014, 16:56
 
Fecha de Ingreso: julio-2006
Ubicación: Barcelona
Mensajes: 244
Antigüedad: 17 años, 9 meses
Puntos: 32
Respuesta: Crear plantilla dynamic. Lo que viene a ser tuple para struct, hacerlo par

De momento he cambiado un poco de estrategia ya que el constructor copia de la unión me estaba dando dolores de cabeza. Ahora tengo esto:

Código C++:
Ver original
  1. #define stringify(s) istringify(s)
  2. #define istringify(s) #s
  3.  
  4. template <class... Ts> union variant {};
  5.  
  6. template<>
  7. union variant<> {
  8.   variant() {};
  9.   ~variant() {};
  10. };
  11.  
  12. template <class T, class... Ts>
  13. union variant<T, Ts...> {
  14.   variant() {};
  15.   ~variant() {};
  16.  
  17.   T head;
  18.   variant<Ts... > tail;
  19. };
  20.  
  21.  
  22. template<typename... Ts>
  23. struct dynamic {
  24.     std::string type;
  25.     variant<Ts...> vnt;
  26. };
  27.  
  28. int main() {
  29.     dynamic<int, std::string> test;
  30.     return 0;
  31. }

Ahora toca definir constructores para dynamic al estilo:

Código C++:
Ver original
  1. template<typename U, typename T, typename... Ts>
  2. typename dynamic<T, Ts>::dynamic(const U& u) :  
  3.   /* si U == T --> type(stringify(U)), vnt(u) <-- constuctor no definido aún */
  4.   /* si U != T -->  algun tipo de recursividad que tengo que pensar */
  5. { }
Mañana sigo pensando.

Un saludo!
__________________
github.com/xgbuils | npm/xgbuils
  #4 (permalink)  
Antiguo 01/12/2014, 03:00
 
Fecha de Ingreso: julio-2006
Ubicación: Barcelona
Mensajes: 244
Antigüedad: 17 años, 9 meses
Puntos: 32
Respuesta: Crear plantilla dynamic. Lo que viene a ser tuple para struct, hacerlo par

La idea es hacer algo así:
Código C++:
Ver original
  1. template <typename T, typename... Ts>
  2. union variant<T, Ts...> {
  3.   variant() {};  
  4.  
  5.   template<typename U>
  6.   std::conditional<
  7.     std::is_same<U, T>,
  8.     // si U == T, es el tipo correcto en el campo head actual
  9.     typename variant(const U& u) : head(u)
  10.     { },
  11.     // si U != T, no es el tipo correcto así que hay que seguir buscando
  12.     typename variant(const U& u) : tail(u)
  13.     { }
  14.   >
  15.  
  16.   ~variant() {};
  17.  
  18.   T head;
  19.   variant<Ts... > tail;
  20. };
pero esto no compila.
__________________
github.com/xgbuils | npm/xgbuils
  #5 (permalink)  
Antiguo 01/12/2014, 03:57
 
Fecha de Ingreso: octubre-2014
Ubicación: Madrid
Mensajes: 1.212
Antigüedad: 9 años, 6 meses
Puntos: 204
Respuesta: Crear plantilla dynamic. Lo que viene a ser tuple para struct, hacerlo par

Si lo haces para practicar templates me parece una práctica con bastante profundidad... ahora, a efectos prácticos creo que es mejor tener una clase para encapsular ese "union" por varias razones:
  • No hay mucha diferencia a efectos de consumo de memoria entre declarar 20 tipos en un union o declarar únicamente 3
  • Si intentas usar tipos no básicos "strings, clases propias" vas a tener que especializar el template, complicando el mantenimiento del código
  • Un template al estilo "tuple", si bien puede ser muy útil en determinadas ocasiones, impone mucha rigidez al código. No puedes hacer un get iterativo de un "tuple" porque el "index" del get tiene que estar especificado en tiempo de compilación.
  • Tienes que estar siempre pendiente del tipo de dato almacenado en cada posición del "union" para evitar conversiones no deseadas.

Si revisas el código fuente de "tuple" verás un poco la estructura que tiene que montar para funcionar... no es moco de pavo. En el caso de mi compilador, la implementación hace uso de media docena de archivos, con uso de macros y todo...

Un problema fundamental de este tipo de diseños es que la programación de templates en C++ acaba degenerando con gran facilidad en un código oscuro en el que la única persona capacitada para editar el código es el propio creador... y eso si aún se sigue acordando del diseño.
  #6 (permalink)  
Antiguo 01/12/2014, 08:08
 
Fecha de Ingreso: julio-2006
Ubicación: Barcelona
Mensajes: 244
Antigüedad: 17 años, 9 meses
Puntos: 32
Respuesta: Crear plantilla dynamic. Lo que viene a ser tuple para struct, hacerlo par

Vaya, pero yo no quería rendirme tan fácilmente. No sé que me pasa que siempre que cuando trato de hacer cosas un poco más complejas con plantillas siempre encuentro problemas que hacen siempre retirarme.

Ahora por ejemplo, olvidando por un momento el tema de tupla-unión. Me encuentro con esta problemática:

Código C++:
Ver original
  1. #include <type_traits>
  2.  
  3. class A {
  4.     public:
  5.         // un constructor con cualquier parámetro
  6.         template<typename U>
  7.         void foo(const U& u) {};
  8.        
  9.         // testeando que std::enable_if funcione bien
  10.         void bar(const std::enable_if<true, int>::type& u) {};
  11.  
  12.         // probando ahora std::enable_if con una template... error
  13.         template<typename U>
  14.         void foobar(const typename std::enable_if<true, U>::type&) {};
  15. };
  16.  
  17. int main() {
  18.     A x;
  19.     x.foo(3); // OK
  20.     x.bar(6); // OK
  21.     x.foobar(9); // error
  22.     return 0;
  23. }

No entiendo por qué el error, echo en falta que C++ responda de manera más intuitiva.

Un saludo!
__________________
github.com/xgbuils | npm/xgbuils
  #7 (permalink)  
Antiguo 01/12/2014, 08:22
 
Fecha de Ingreso: octubre-2014
Ubicación: Madrid
Mensajes: 1.212
Antigüedad: 9 años, 6 meses
Puntos: 204
Respuesta: Crear plantilla dynamic. Lo que viene a ser tuple para struct, hacerlo par

Esto puede que te aclare un poco:

http://stackoverflow.com/questions/1...ion-signatures

Y estoy totalmente de acuerdo con que C++ debería dar mensajes un poco más consistentes...
  #8 (permalink)  
Antiguo 01/12/2014, 10:19
 
Fecha de Ingreso: julio-2006
Ubicación: Barcelona
Mensajes: 244
Antigüedad: 17 años, 9 meses
Puntos: 32
Respuesta: Crear plantilla dynamic. Lo que viene a ser tuple para struct, hacerlo par

He leído el enlace y no acabo de entender que es lo que debo aplicar. La persona está preguntando por cual de las 3 maneras es preferible usar std::enable_if.

La respuesta más puntuada habla de usar enable_if como un parámetro de un template. Entonces miro el código correspondiente:
Código C++:
Ver original
  1. template<typename T>
  2. struct Check2
  3. {
  4.    template<typename U = T, typename std::enable_if<
  5.             std::is_same<U, int>::value, int>::type = 0>
  6.    U read() { return 42; }
  7.  
  8.    template<typename U = T, typename std::enable_if<
  9.             std::is_same<U, double>::value, int>::type = 0>
  10.    U read() { return 3.14; }  
  11. };

Y observo algo que no acabo de entender como se comporta. ¿Se supone que si el primer "parámetro de enable_if es false, la plantilla donde está enable_if no se compilará? Hago la siguiente prueba basado en eso:
Código C++:
Ver original
  1. template<typename T>
  2. struct A
  3. {
  4.    template<typename U, typename std::enable_if<
  5.             std::is_same<U, int>::value, int>::type = 0>
  6.    U foo(const U& u) { return 42; }
  7.  
  8.    template<typename U, typename std::enable_if<
  9.             std::is_same<U, double>::value, int>::type = 0>
  10.    U foo(const U& u) { return 3.14; }  
  11. };
  12.  
  13. #include <type_traits>
  14. #include <string>
  15.  
  16. int main() {
  17.     A<int> x;
  18.     std::cout << x.foo(1.8) << std::endl; // 3.14
  19.     std::cout << x.foo(2) << std::endl; // 42
  20.     return 0;
  21. }

Hago una prueba más avanzada comparando el tipo T con el tipo U y si son iguales se implementará una función, si no son iguales se implementará otra:

Código C++:
Ver original
  1. template<typename T>
  2. struct A
  3. {
  4.    template<typename U, typename std::enable_if<
  5.             std::is_same<U, T>::value, int>::type = 0>
  6.    int foo(const U& u) { return 42; }
  7.  
  8.    template<typename U, typename std::enable_if<
  9.             !std::is_same<U, T>::value, int>::type = 0>
  10.    double foo(const U& u) { return 3.14; }  
  11. };
  12.  
  13. int main() {
  14.     A<double> x;
  15.     std::cout << x.foo(1.8) << std::endl; // 42
  16.     std::cout << x.foo(2) << std::endl; // 3.14
  17.     A<int> y;
  18.     std::cout << y.foo(1.8) << std::endl; // 3.14
  19.     std::cout << y.foo(2) << std::endl; // 42
  20.     return 0;
  21. }

Uau! funciona! Con esto ya puedo seguir avanzando.
__________________
github.com/xgbuils | npm/xgbuils
  #9 (permalink)  
Antiguo 01/12/2014, 11:04
 
Fecha de Ingreso: julio-2006
Ubicación: Barcelona
Mensajes: 244
Antigüedad: 17 años, 9 meses
Puntos: 32
Respuesta: Crear plantilla dynamic. Lo que viene a ser tuple para struct, hacerlo par

¡Genial!

Esto ya empieza a tener cara y ojos:

Código C++:
Ver original
  1. #include <type_traits>
  2.  
  3. template <typename... Ts> union variant {};
  4.  
  5. template<>
  6. union variant<> {
  7.     public:
  8.         template<typename U>
  9.         variant(const U&) {};
  10. };
  11.  
  12. template<typename T, typename... Ts>
  13. union variant<T, Ts...>
  14. {
  15.     public:
  16.         template<typename U, typename std::enable_if<std::is_same<U, T>::value, int>::type = 0>
  17.         variant(const U& u) : head(u) { }
  18.  
  19.         template<typename U, typename std::enable_if<!std::is_same<U, T>::value, int>::type = 0>
  20.         variant(const U& u) : tail(u) { }
  21.  
  22.         T head;
  23.         variant<Ts...> tail;
  24. };
  25.  
  26. template<typename U, typename T, typename... Ts,
  27.     typename std::enable_if<std::is_same<U, T>::value, int>::type = 0>
  28. U get(const variant<T, Ts...>& vnt) {
  29.     return vnt.head;
  30. }
  31.  
  32. template<typename U, typename T, typename... Ts,
  33.     typename std::enable_if<!std::is_same<U, T>::value, int>::type = 0>
  34. U get(const variant<T, Ts...>& vnt) {
  35.     return get<U>(vnt.tail);
  36. }
  37.  
  38. int main() {
  39.     variant<double, int> x = 1.8;
  40.     std::cout << get<double>(x) << std::endl; // 1.8
  41.     std::cout << get<int>(x) << std::endl; // no se almacena igual un int que un double, sale un numero raro
  42.  
  43.     variant<double, int> y = "blabla"; // esto debería dar error pero no lo da.
  44.     //std::cout << get<unsigned>(x) << std::endl; // esto da error por no existir unsigned
  45.     return 0;
  46. }

Ahora toca pensar como restringir el numero de constructores a los definidos en el template.

Muchas gracias, eferion.
__________________
github.com/xgbuils | npm/xgbuils
  #10 (permalink)  
Antiguo 01/12/2014, 12:45
 
Fecha de Ingreso: octubre-2014
Ubicación: Madrid
Mensajes: 1.212
Antigüedad: 9 años, 6 meses
Puntos: 204
Respuesta: Crear plantilla dynamic. Lo que viene a ser tuple para struct, hacerlo par

Cita:
Iniciado por Pantaláimon Ver Mensaje
¡Genial!
Enhorabuena!!!!

Cita:
Iniciado por Pantaláimon Ver Mensaje
Muchas gracias, eferion.
Si no he hecho nada!!! Me habría gustado ayudarte más, pero los templates son algo para sentarse con calma y tiempo y es algo de lo que ahora mismo ando algo escaso :(

Etiquetas: char, clase, dynamic, hacerlo, int, plantilla, struct
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




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