2010-02-07 16 views
6

Me encanta Haskell patrón de estilo de juego.coincidencia de patrones de estilo en C++?

tengo mi código C++ de la siguiente manera:

ObjectPtr ptr; 
if(ptr.isType<Foo>()) { // isType returns a bool 
    Ptr<Foo> p = ptr.convertAs<Foo>(); // convertAs returns a Ptr<Foo> 
    ...... 
} 
if(ptr.isType<Bar>()) { 
    Ptr<Bar> p = ptr.convertAs<Bar>(); 
    ...... 
} 

Ahora, ¿hay ninguna macro que puedo hacer para simplificar definir esto? He estado reflexionando sobre esto por un tiempo, pero no puedo simplificarlo más.

Gracias!

+2

Supongo que te falta '()' después de 'isType'. – AndiDog

+0

Buena llamada. +1 en el comentario. En – anon

+0

Mach7, su código se verá de la siguiente manera:

 Match(*ptr) { Case(Foo) return match0.foo_member(); Case(Bar) return match0.bar_member(); Otherwise() return 0; } EndMatch 
Ver https://github.com/solodon4/Mach7 y mi respuesta sobre Mach7 continuación – solodon

Respuesta

2

Estoy asumiendo que su plantilla Ptr tiene el concepto de un puntero NULL.

ObjectPtr ptr; 
if(Ptr<Foo> p = ptr.convertAs<Foo>()) { // convertAs returns a NULL pointer if the conversion can't be done. 
    ...... 
} 
if(Ptr<Bar> p = ptr.convertAs<Bar>()) { 
    ...... 
} 

Aunque, como han dicho otros, el cambio en el tipo suele ser una señal de que estás haciendo algo mal en C++. Debería considerar el uso de funciones virtuales en su lugar.

+0

¿Esto no hace surgir otra pregunta? "¿Cómo escribo convertAs() para hacer eso?" –

+0

@Neil: En realidad, su código original no presupone la existencia de un convertAs. Esto presumiblemente usaría un dynamic_cast, que produce un puntero nulo si no puede convertir un puntero al tipo de destino. Como tal, las posibilidades son bastante buenas de que ya estén presentes, o bastante fáciles de implementar si no están presentes. –

+2

La mejor parte es que en C++ 0x puede reemplazar 'Ptr ' con 'auto'. – Omnifarious

7

dynamic_cast parece hacer lo que quiera

struct A { 
    virtual ~A() {} 
}; 

struct B : struct A { ... }; 
struct C : struct A { ... }; 

A * a = new C; 

if (C * c = dynamic_cast<C*>(a)) { 
    c->someCfunc(); 
} 
else if (B * b = dynamic_cast<B*>(a)) { 
    b->someBfunc(); 
} 
else { 
    throw "Don't know that type"; 
} 
+0

1 de esfuerzo. Sin embargo, estoy usando punteros inteligentes. – anon

+0

Luego realice un lanzamiento dinámico en el puntero contenido; por ejemplo, si usa std :: auto_ptr(), hágalo en el resultado de llamar a get(). –

7

Me encanta Haskell patrón de estilo de juego.

Luego escriba su programa en Haskell.

Lo que estamos tratando de hacer es un interruptor más de un tipo. Eso es algo común que hacen las personas si quieren evitar las funciones virtuales. Ahora, estos últimos son una piedra angular de lo que se trata OO en C++. Si desea evitarlos, ¿por qué programa en C++?


En cuanto a por qué esto está mal visto: Imagine que tiene una gran cantidad de código como este

if(ptr.isType<Foo>()) ... 
if(ptr.isType<Bar>()) ... 

untado todo su código y luego alguien viene y añade Baz a los posibles tipos que ptr poder representar. Ahora usted está de caza a través de una base de código grande, tratando de encontrar todos aquellos lugares donde se conmuta un tipo, y tratando de averiguar cuáles necesita añadir a Baz.

Y, como Murphy lo tiene, cuando termines, también se incluirá Foz como tipo. (O, pensando de nuevo, si Murphy tiene su manera que se arrastra en antes de que tuviera la oportunidad de añadir demasiado completa Baz.)

+2

Control de memoria de bajo nivel de C. Constructores y Destructores de objetos en la Pila <- esto es sorprendente, ya que proporciona punteros inteligentes, RAII, ...; biblioteca de plantillas estándar (¿utiliza esto alguna de las funciones virtuales?). +1 asumir la respuesta fue una pregunta genuina, no troll. – anon

+1

Ciertamente no era una pregunta genuina, pero tampoco era un troll. Fué una pregunta retórica. Para ser claro: _No deberías hacer esto en C++ _. Para eso se inventaron las funciones virtuales; ponen todo el código relacionado con objetos del tipo 'Foo' en' Foo' en sí, y todo lo relacionado con 'Baz' en' Baz'.Esto no siempre es lo mejor que puedes obtener (prueba, por ejemplo, la motivación detrás del patrón de visitante), pero es el valor predeterminado con el que deberías comenzar en C++. – sbi

+0

'' Ahora, estos últimos son la piedra angular de lo que se trata OO en C++. "Entonces, ¿por qué deberíamos usar la metaprogramación de plantillas cuando es básicamente FP y no OO en absoluto, todos deberíamos ir a Haskel en lugar de usar STL, aumentar o usar nuestras propias plantillas ... _rolleyes_ Además, solo alguien completamente ignorante de C++ haría esa pregunta, [como este tipo (enlace a pdf)] (http://www.stroustrup.com/OpenPatternMatching.pdf) totalmente despistado! – Trinidad

2

Una pensar esta macro hace precisamente lo que quiere:

#define DYN_IF(dest_type, dest_ptr, src_ptr)         \ 
    if((src_ptr).isType<dest_type>())          \ 
     if(int dest_type##dest_ptr = 1)          \ 
     for(Ptr<dest_type> dest_ptr = (src_ptr).convertAs<dest_type>();  \ 
      dest_type##dest_ptr;            \ 
      dest_type##dest_ptr=0)           

Uso:

ObjectPtr ptr; 
DYN_IF(Foo, foo_ptr, ptr) { 
    // foo_ptr is Ptr<Foo> 
} 
DYN_IF(Bar, bar_ptr, ptr) // Works without braces too for single statement 
    // bar_ptr is Ptr<Bar> 

no recomendaría este tipo de cosas en el código que está destinado a ser leído por otra persona , pero ya que ha mencionado la palabra "macro" ...

Además, yo no pretendo que esto tiene algo que ver con la coincidencia de patrones en el estilo Haskell/OCaml. Compruebe Scala si quieres un lenguaje que tiene una semántica similar a C++ (bueno, más o menos) y la verdadera coincidencia de patrones.

5

Intentar simular un estilo de coincidencia de patrón en C++ utilizando RTTI es una idea clara, pero tiene deficiencias, porque hay algunas diferencias significativas entre los constructores de tipo de estilo Haskell y Standard ML y las subclases de C++. (Nota: la siguiente, yo uso la sintaxis ml estándar porque estoy más cómodo con él.)

  • En Haskell y Standard ML, la coincidencia de patrones se puede unir a los valores anidados variables del patrón para usted (por ejemplo, el patrón a::b::c::ds une la primeros tres elementos de la lista a a, b y c, y el resto de la lista a ds). En C++, aún tendrá que buscar en las estructuras anidadas reales, a menos que usted u otra persona presente macros mucho más complicadas de las que se han propuesto aquí.
  • En Haskell y Standard ML, una declaración tipo de datos de constructor de tipo como datatype 'a option = NONE | SOME of 'a define un nuevo tipo: 'a option. Los constructores NONE y SOME no son tipos, son valores con los tipos 'a option y 'a -> 'a option, respectivamente. En C++, cuando define subclases como Foo y Bar para simular constructores de tipo, obtiene nuevos tipos.
  • En Haskell y Standard ML, los constructores como SOME son funciones de primera clase que construyen valores del tipo de datos al que pertenecen. Por ejemplo, map SOME tiene el tipo 'a list -> 'a option list. En C++, usando subclases para simular constructores de tipo, no obtienes esta habilidad.
  • En Haskell y ML estándar, los tipos de datos están cerrados, por lo que nadie puede agregar más constructores sin cambiar la declaración original, y el compilador puede verificar en tiempo de compilación que la coincidencia de patrón maneja todos los casos. En C++, tienes que salir de tu camino para restringir quién puede subclasificar tu clase base.

Al final, ¿obtiene suficiente beneficio de la simulación de patrones simulada en comparación con el uso del polimorfismo C++ de una manera más típica? ¿Vale la pena usar macros para hacer que la coincidencia de patrones simulada sea un poco más concisa (al mismo tiempo que la ofusca para todos los que leen el código)?

4

Somos coautores de una biblioteca de coincidencias de patrones para C++ que le permite realizar coincidencias de patrones y análisis de tipos de manera muy eficiente. La biblioteca, llamada Mach7, se ha lanzado bajo la licencia BSD y está disponible en GitHub: https://github.com/solodon4/Mach7. Puede encontrar videos, carteles, diapositivas, documentos y el código fuente allí. Actualmente es compatible con GCC 4.4+, Clang 3.4+ y Visual C++ 2010+. No dude en hacer preguntas sobre la biblioteca mediante la presentación de un problema GitHub contra su repositorio.

Cuestiones relacionadas