tutorial smart remix programador online español curso c++ templates oop

c++ - remix - smart contracts ethereum



¿Se puede usar el polimorfismo de plantilla en lugar del polimorfismo OO? (3)

Encuentro que las plantillas y el polimorfismo funcionan bien juntos. En el ejemplo, si al código del cliente no le importa qué parámetros de plantilla está utilizando Interpolator, introduzca una clase base abstracta en la que la plantilla sea subclases. P.ej:

class Interpolator { public: virtual Value GetValue (const double) = 0; }; template<class TCacheStrategy, class TDataSource> class InterpolatorImpl : public Interpolator { public: InterpolatorImpl (); Value GetValue(const double); }; void main() { int param = 1; Interpolator* interpolator = 0; if (param==1) interpolator = new InterpolatorImpl<InMemoryStrategy,TextFileDataSource> (); else if (param==2) interpolator = new InterpolatorImpl<InMemoryStrategy,OdbcDataSource> (); else if (param==3) interpolator = new InterpolatorImpl<NoCachingStrategy,RestDataSource> (); while (true) { double input = WaitForRequest(); SendRequest( interpolator->GetValue (input)); } }

Yo uso este idioma bastante. Oculta muy bien las cosas de la plantilla del código del cliente.

Tenga en cuenta que no estoy seguro de que este uso de las plantillas realmente clasifique como "meta-programación". Por lo general, reservo ese término grandioso para el uso de trucos de plantillas de tiempo de compilación más sofisticados, especialmente el uso de condicionales, definiciones recursivas, etc., para computar efectivamente las cosas en tiempo de compilación.

Estoy intentando concentrarme en aplicar la programación de plantillas (y, en algún momento futuro, la metaprogramación de plantillas) en escenarios del mundo real. Un problema que estoy encontrando es que las plantillas y el polimorfismo en C ++ no siempre juegan juntos como yo quiero.

Mi pregunta es si la forma en que estoy tratando de aplicar la programación de la plantilla es incorrecta (y debo usar OOP) o si todavía estoy atrapado en la mentalidad de OOP

En este caso particular, estoy tratando de resolver un problema usando el patrón de estrategia. Sigo corriendo en el problema donde termino queriendo que algo se comporte de forma polimórfica que las plantillas no parecen admitir.

Código OOP usando la composición:

class Interpolator { public: Interpolator(ICacheStrategy* const c, IDataSource* const d); Value GetValue(const double); } void main(...) { Interpolator* i; if(param==1) i = new Interpolator(new InMemoryStrategy(...), new TextFileDataSource(...)); else if(param==2) i = new Interpolator(new InMemoryStrategy(...), new OdbcDataSource(...)); else if(param==3) i = new Interpolator(new NoCachingStrategy(...), new RestDataSource(...)); while(run) { double input = WaitForRequest(); SendRequest( i->GetValue(input)); } }

Versión potencial de la plantilla:

class Interpolator<class TCacheStrategy, class TDataSource> { public: Interpolator(); Value GetValue(const double); //may not be the best way but void ConfigCache(const& ConfigObject); //just to illustrate Cache/DS void ConfigDataSource(const& ConfigObject); //need to configured } //Possible way of doing main? void main(...) { if(param==1) DoIt(Interpolator<InMemoryStrategy,TextFileDataSource>(),c,d); else if(param==2) DoIt(Interpolator<InMemoryStrategy,OdbcDataSource>(),c,d) else if(param==3) DoIt(Interpolator<NoCachingStrategy,RestDataSource>(),c,d) } template<class T> void DoIt(const T& t, ConfigObject c, ConfigObject d) { t.ConfigCache(c); t.ConfigDataSource(c); while(run) { double input = WaitForRequest(); SendRequest( t.GetValue(input)); } }

Cuando trato de convertir la implementación OOP en una implementación basada en plantillas, el código Interpolator se puede traducir sin mucho dolor. Básicamente, reemplace las "interfaces" con los parámetros de tipo de plantilla y agregue un mecanismo para pasar una instancia de Strategy / DataSource o parámetros de configuración.

Pero cuando llego a la "principal", no me queda claro cómo se debe escribir para aprovechar las plantillas al estilo de la meta programación de plantillas. A menudo quiero usar el polimorfismo, pero no parece jugar bien con las plantillas (a veces, siento que necesito los genéricos de borrado de tipo de Java ... ugh).

Cuando a menudo descubro que quiero hacer es tener algo como TemplateType<?,?> x = new TemplateType<X,Y>() donde a x no le importa lo que es X, Y.

De hecho, este suele ser mi problema al usar plantillas.

  1. ¿Necesito aplicar un nivel más de plantillas?
  2. ¿Estoy intentando usar mi nueva y brillante llave de plantilla eléctrica para instalar un clavo OOP en una ranura PCI?
  3. ¿O simplemente estoy pensando en todo esto mal cuando se trata de la programación de plantillas?

[Editar] Algunas personas han señalado que esto no es en realidad una metaprogramación de plantillas, por lo que he modificado la pregunta un poco. Tal vez eso sea parte del problema: todavía me he quejado de lo que realmente es TMP.


Las plantillas a veces se denominan polimorfismo estático (o de tiempo de compilación), así que sí, a veces se pueden usar en lugar del polimorfismo OOP (dinámico). Por supuesto, requiere que los tipos se determinen en tiempo de compilación, en lugar de en tiempo de ejecución, por lo que no puede reemplazar completamente el polimorfismo dinámico.

Cuando a menudo encuentro que quiero hacer es tener algo como TemplateType x = new TemplateType () donde a x no le importa qué es X, Y.

Sí, eso no es posible. Tienes que hacer algo similar a lo que tienes con la función DoIt (). A menudo, creo que, de todos modos, eso termina siendo una solución más limpia (terminas con funciones más pequeñas que hacen solo una cosa cada una, generalmente algo bueno). Pero si los tipos solo se determinan en tiempo de ejecución (como i con i en la versión OOP de su función principal), las plantillas no funcionarán.

Pero en este caso, creo que la versión de su plantilla resuelve bien el problema y es una buena solución por derecho propio. (Aunque como lo menciona Onebyone, sí significa que el código se instancia para las tres plantillas, lo que en algunos casos podría ser un problema)


Las plantillas proporcionan polimorfismo estático: se especifica un parámetro de plantilla en el momento de la compilación que implementa la estrategia. No proporcionan polimorfismo dinámico, donde se suministra un objeto en tiempo de ejecución con funciones de miembro virtual que implementan la estrategia.

El código de su plantilla de ejemplo creará tres clases diferentes, cada una de las cuales contiene todo el código Interpolator, compilado usando diferentes parámetros de plantilla y posiblemente incorpore el código de ellas. Probablemente no sea lo que quieres del POV de tamaño de código, aunque no hay nada categóricamente incorrecto en ello. Suponiendo que estaba optimizando para evitar la sobrecarga de llamadas de funciones, entonces podría ser una mejora en el polimorfismo dinámico. Más probable es que sea una exageración. Si desea utilizar el patrón de estrategia de forma dinámica, no necesita plantillas, solo haga llamadas virtuales cuando sea relevante.

No puede tener una variable de tipo MyTemplate<?> (Excepto que aparece en otra plantilla antes de ser instanciada). MyTemplate<X> y MyTemplate<Y> son clases completamente no relacionadas (incluso si X e Y están relacionadas), que quizás tengan funciones similares si se crea una instancia desde la misma plantilla (que no es necesario que sean) podría ser una especialización). Incluso si lo están, si el parámetro de la plantilla está involucrado en las firmas de cualquiera de las funciones miembro, entonces esas funciones no son las mismas, solo tienen los mismos nombres. Por lo tanto, desde el punto de vista del polimorfismo dinámico, las instancias de la misma plantilla se encuentran en la misma posición que cualquiera de las dos clases; solo se pueden jugar si se les asigna una clase base común con algunas funciones miembro virtuales.

Entonces, podrías definir una clase base común:

class InterpolatorInterface { public: virtual Value GetValue(const double) = 0; virtual void ConfigCache(const& ConfigObject) = 0; virtual void ConfigDataSource(const& ConfigObject) = 0; virtual ~InterpolatorInterface() {} };

Entonces:

template <typename TCacheStrategy, typename TDataSource> class Interpolator: public InterpolatorInterface { ... };

Ahora está utilizando plantillas para crear sus diferentes tipos de Interpolator de acuerdo con lo que se conoce en el momento de la compilación (por lo que las llamadas del interpolador a las estrategias no son virtuales), y está utilizando un polimorfismo dinámico para tratarlas de la misma manera, aunque no se sabe hasta el tiempo de ejecución cuál desea (por lo que las llamadas del cliente al interpolador son virtuales). Solo debes recordar que las dos técnicas son bastante independientes y que las decisiones sobre dónde usarlas no tienen nada que ver.

Por cierto, esto no es una meta-programación de plantillas, solo está usando plantillas.

Editar. En cuanto a lo que es TMP, aquí está el ejemplo introductorio canónico:

#include <iostream> template<int N> struct Factorial { static const int value = N*Factorial<N-1>::value; }; template<> struct Factorial<0> { static const int value = 1; }; int main() { std::cout << "12! = " << Factorial<12>::value << "/n"; }

¡Observa que 12! ha sido calculado por el compilador y es una constante de compilación. Esto es emocionante porque resulta que el sistema de plantillas de C ++ es un lenguaje de programación Turing-complete, que no es el preprocesador de C. Sujeto a los límites de recursos, puede realizar cálculos arbitrarios en el momento de la compilación, evitando la sobrecarga del tiempo de ejecución en situaciones en las que conoce las entradas en el momento de la compilación. Las plantillas pueden manipular sus parámetros de plantilla como un lenguaje funcional, y los parámetros de plantilla pueden ser enteros o tipos. O funciones, aunque esas no pueden ser "llamadas" en tiempo de compilación. U otras plantillas, aunque no se pueden "devolver" como miembros estáticos de una estructura.