c++ - programming - programacion por metas online
¿Cuándo/por qué(si es que alguna vez) debería pensar en hacer Programación Genérica/Programación Meta? (9)
Creo que, si usted no es un desarrollador de bibliotecas, es mejor olvidarse de la metaprogramación de plantillas. Creo que es inútil en el código de producción, da más problemas y luego se beneficia: "Una vez que comenzó a usarlo, pierde flexibilidad al estar atado a esta solución. Es muy difícil desprenderse del código de plantilla después de comenzó a usarlo. Y esto es un problema, ya que las plantillas no son lo suficientemente flexibles ".
PS Por supuesto, no me refiero a los contenedores de plantillas, swaps, ... sino al código de Alexandrescu
En mi opinión, OOPS, los patrones de diseño tienen sentido y he podido aplicarlos prácticamente.
Pero cuando se trata de "programación genérica / meta programación" del tipo Modern C ++, me quedo confundido.
- ¿Es un nuevo paradigma de programación / diseño?
- ¿Se limita a "desarrollo de bibliotecas"? Si no es así, qué situaciones de diseño / codificación requieren el uso de una programación meta / programación genérica.
- ¿Usar plantillas significa que estoy haciendo programación genérica?
He buscado mucho en Google sobre este tema, pero no entiendo la GRAN IMAGEN. También vea este post .
Después de leer las discusiones aquí debajo, hasta ahora, estoy seguro (aún podría no ser correcto):
a) La programación genérica y la meta programación son dos conceptos diferentes.
Cuando te queda bastante tiempo y quieres jugar .
Cuando necesita aumentar su algoritmo y no desea una sobrecarga de las llamadas a funciones virtuales, puede reemplazar el enlace de tiempo de ejecución con enlaces de tiempo de compilación.
Cuando:
La meta programación de plantillas es una forma de escribir programas que son interpretados por el compilador. Es un nivel de abstracción superior. Puedes hacer todo tipo de cosas raras y funky con eso. Las bibliotecas de boost contienen muchos ejemplos:
- ¿No te gusta que c ++ esté tipeado estáticamente? Use boost :: any.
- Me encanta el cálculo lambda, pero debes c ++? Use boost: lambda.
- ¿Tienes un EBNF y necesitas un analizador? Usa boost :: spirit.
Por qué:
Es genial. Puedes impresionar tus fechas (tal vez).
Ejemplo simple:
template<typename T>
void
swap(T& var1, T& var2)
{
T var3 = var1;
var1 = var2;
var2 = var3;
}
int a = 2;
int b = 3;
swap(a, b);
float c = 400.0f;
float d = 500.0f;
swap(c, d);
Intercambia los valores de 2 variables que tienen el mismo tipo.
Con las plantillas evitamos crear funciones explícitamente para varias combinaciones de tipos como estas:
void
swap(int& var1, int& var2)
{
int var3 = var1;
var1 = var2;
var2 = var3;
}
void
swap(float& var1, float& var2)
{
float var3 = var1;
var1 = var2;
var2 = var3;
}
El compilador creará automáticamente las funciones anteriores, en mi ejemplo, si se llama a swap () en algún lugar del código con las variables int o float .
La metaprogramación de plantillas de C ++ es una poderosa técnica de ofuscación de código que se aplica a una variedad de aplicaciones:
- Cuando desee escribir un código que nadie más en su equipo pueda entender
- Cuando desee un código que no podrá entender 7 días después de haberlo escrito
- Cuando el rendimiento del código es más importante para usted que la capacidad de mantenimiento.
- Cuando desee poder listar "Metaprogramación de plantillas" como una habilidad en su currículum.
- Cuando necesite escribir código que probablemente no funcione en muchos compiladores del mundo real
- Si prefiere comer cuchillas de afeitar que utilizar macros de preprocesador
Otro caso:
- Si quieres saber cómo funcionan las bibliotecas Boost bajo el capó, o quieres contribuir a ellas.
La distinción entre "programación genérica" (piense en los contenedores STL, auto_ptrs, etc.), que es lo que las plantillas de C ++ se diseñaron para lograr y la "metaprogramación de la plantilla" (uso de la plantilla del sistema para que el compilador "ejecute algoritmos" para usted) es importante.
Estoy a favor de lo primero, pero me resulta difícil ver los beneficios del mundo real de lo segundo.
La metaprogramación es un tema bastante exótico. Es interesante aprender sobre él, es una herramienta poderosa y, en ocasiones, puede resultarle útil. Pero nunca será la herramienta más utilizada en tu caja de herramientas. A veces, es posible que desee que su código actúe en un rango de tipos no relacionados con diferentes propiedades, y ahí es donde entra la metaprogramación. Con un poco de trucos, puede escribir una sobrecarga de una función que solo está disponible si el tipo de argumento es integral , o si es un puntero, o si es de tipo X, Y o Z (quizás ignorando la constancia en Z).
Es esencialmente la programación con tipos. Normalmente, su programa puede hacer cosas como tomar dos números y producir un tercer número, o decirle si un número satisface algún requisito. La metaprogramación puede tomar dos tipos y producir un tercer tipo, o decirle si un tipo satisface algún requisito. Y sí, es probablemente más útil en el desarrollo de bibliotecas. Pero, de nuevo, la mayoría del código podría considerarse un código de biblioteca. Podría decir que todo lo que está fuera de su función main () es un código de biblioteca.
Por lo general, si desea resolver un problema a través de la metaprogramación, probablemente querrá usar las bibliotecas de impulso relevantes para hacer el trabajo pesado. Boost.TypeTraits y, por supuesto, Boost.Mpl realmente puede simplificar las cosas para ti. Pero no es algo que deba saber, y no es algo que pueda necesitar muy a menudo.
La programación genérica está relacionada (y en algunos casos puede usar metaprogramación bajo el capó para convertirse en realmente genérica, por ejemplo, la biblioteca estándar usa un toque de metaprogramación para convertir los punteros sin formato en iteradores válidos, lo que es necesario para que el concepto "iterador" sea genérico) , pero no del todo lo mismo. Y es mucho más utilizado.
Cada vez que crea una instancia de un std::vector
, utiliza la programación genérica. Cada vez que usa un par de iteradores para procesar una secuencia de valores, utiliza la programación genérica. La programación genérica es solo la idea de que su código debería ser lo más genérico posible y debería funcionar independientemente de los tipos que se le asignen. Un std :: vector no requiere que el tipo contenido implemente una interfaz "ICanBeContained" (¿recuerda cómo Java requiere que todo se derive de Object para que se almacene en una clase de contenedor? ¿Qué significa que los tipos primitivos se recuadran, y que perdemos seguridad de tipo. Eso no es genérico, y es una restricción sin sentido.)
El código para iterar sobre una secuencia usando iteradores es genérico y funciona con cualquier tipo de iteradores, o incluso con punteros simples.
La programación genérica es muy útil y, con frecuencia, puede reemplazar en gran medida la POO. (vea el ejemplo anterior. ¿Por qué escribiría un contenedor que requería que los tipos contenidos implementaran una interfaz, si puedo evitar esa limitación?)
A menudo, cuando usa interfaces en OOP, no es para permitir que el tipo cambie durante el tiempo de ejecución (aunque, por supuesto, eso también ocurre de vez en cuando), sino para permitirle intercambiar otro tipo en tiempo de compilación (tal vez inyectar un objeto simulado durante las pruebas, en lugar de utilizar la implementación de pleno derecho), o simplemente para desacoplar dos clases. La programación genérica puede hacer eso, sin tener que hacer el tedioso trabajo de definir y mantener la interfaz. En esos casos, la programación genérica significa que tiene que escribir y mantener menos código, y obtener un mejor rendimiento y una mayor seguridad de tipos. Así que sí, definitivamente debes sentirte como en casa con la programación genérica. C ++ no es un lenguaje OOP muy bueno. Si desea seguir estrictamente con OOP, debe cambiar a Java u otro lenguaje más corregido por OOP. C ++ le permite escribir código OO, pero a menudo no es la mejor solución. Hay una razón por la que casi toda la biblioteca estándar se basa en la programación genérica, en lugar de la POO. Hay muy poca herencia o polimorfismo en la biblioteca estándar. No lo necesitaron, y el código se volvió más simple de usar y más poderoso sin él.
Y para responder a sus otras preguntas, sí, la programación genérica es casi un paradigma separado. Plantilla de metaprogramación no es. Es una técnica bastante específica para manipular el sistema de tipos, y es muy buena para resolver un pequeño número de problemas. Para ser considerado un paradigma, creo que tendría que ser mucho más útil en general, y un enfoque que puede utilizar básicamente para todo , como funcional, OO o programación genérica.
Creo que xtofl realmente lo definió: la programación genérica consiste en hacer que su código no sea consciente. (A std :: vector no le importa , o necesita saber qué tipo está almacenado en él. Simplemente funciona.)
La metaprogramación, por otro lado, se trata de cálculos de tipo. Dado el tipo T0 y T1, podemos definir un tipo T2, tal como podemos, dados los enteros N0 y N1, podemos definir un N2 que es la suma de N0 y N1.
La biblioteca Boost.Mpl tiene un ejemplo obvio de esto. En su código normal, si tiene los números enteros N0, N1 y N2, puede crear un std :: vector que contenga estos tres valores. Luego puedo usar algún otro algoritmo para calcular un índice y luego extraer el valor almacenado en esa ubicación en el vector.
Dados los tipos T0, T1 y T2, podemos crear un mpl :: vector que contenga estos tres tipos . Ahora puedo usar algún otro algoritmo para calcular un índice en tiempo de compilación y extraer el tipo almacenado en esa ubicación en el vector.
Para plantillas y técnicas avanzadas, recomiendo: Modern C ++ Design , por Andrei Alexandrescu
C ++ es un lenguaje rígido, prácticamente sin capacidades de introspección en tiempo de ejecución. Muchos de los problemas que encontrarás pueden ser tratados con plantillas. Además, el lenguaje de las plantillas está completo, lo que permite generar complejas estructuras de datos y precalcular los valores en tiempo de compilación, entre otras cosas. Para muchos escenarios, puede ser más problemático de lo que vale la pena.
Realmente tiene que diferenciar entre la programación genérica (que es algo que no conoce el tipo) y la metaprogramación, que es una forma perfectamente legal de hacer cálculos dentro del sistema de tipos.
La programación genérica es muy útil cuando encuentras un patrón generalizable en una gran cantidad de código. Si la diferencia en el patrón radica no solo en los valores variables, sino también en diferentes tipos, la programación genérica es útil para refactorizar el código.
La metaprogramación es aplicable en un dominio totalmente diferente. Ese dominio es, de hecho, bastante nuevo y necesita una exploración propia. ¡También es divertido!
Un patrón de metaprogramación muy útil y común es el concepto de Rasgos / Política.
Una respuesta que aún no se ha dado: aunque la programación genérica y la meta-programación son técnicas muy diferentes entre sí, ambas fueron diseñadas para resolver (esencialmente) el mismo problema: cómo deshacerse de la caldera. Estos también están incorporados no solo en una sino en dos acrónimos de los principios de ingeniería de software: DRY (no se repita) y DIE (la duplicación es mala) . Esta es una reformulación moderna de Occam''s Razor , es decir, que "las entidades no deben multiplicarse más allá de la necesidad" ( entia non sunt multiplicanda praeter necessitatem ). [¿Quién sabía que los lógicos del siglo XIV podrían encontrar algo útil para el diseño de software del siglo XXI?].
De todos modos, el punto de ambas técnicas es encontrar formas de unificar códigos que sean "casi iguales" en una sola pieza de código parametrizado. En el caso de la meta-programación, está utilizando la generación de código, por lo que la técnica consiste en especializar una pieza general de código (también conocida como plantilla, o más generalmente una función de generación de código) para un caso específico. En el caso de la programación genérica, intenta utilizar el sistema de tipos para reconocer que varias piezas de código son "iguales" y luego se utiliza el envío basado en tipos para la especialización.
Resulta que estas dos técnicas pueden incluso usarse juntas para hacer cosas más bien elegantes. Vea la programación de etapas múltiples con funtores y mónadas: eliminar la sobrecarga de abstracción del código genérico para un ejemplo de esto. Pero si crees que el STL da miedo, mejor no lo mires ...