Pattern matching style in C ++?

I like the Haskell style pattern matching.

I have C ++ code as follows:

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>(); ...... } 

Now, are there any macros I can do to simplify this? I have been thinking about this for some time, but I cannot simplify it further.

Thanks!

+6
c ++ macros pattern-matching
source share
6 answers

I assume your Ptr template has the concept of a NULL pointer.

 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>()) { ...... } 

Although, as others have noted, the inclusion of a type is usually a sign that you are doing something wrong in C ++. Instead, you should use virtual functions.

+2
source share

dynamic_cast seems to do what you want

 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"; } 
+7
source share

I like the Haskell style pattern matching.

Then write your program in Haskell.

What you are trying to do is type switching. This is a common thing people do if they want to avoid virtual functions. Now the latter is the cornerstone of what OO in C ++ is all about. If you want to avoid them, why are you programming in C ++?


As to why this is underestimated: imagine you have a lot of code like this

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

smeared all over your code, and then someone comes up and adds Baz to the possible types that ptr might be. Now you are looking at a large code base, trying to find all the places where you switched the type, and trying to figure out which ones you need to add Baz to.

And, as Murphy does, only when you are done is Foz added as a type. (Or, thinking again, if Murphy has his way, he crawls before you have a chance too full by adding Baz .)

+7
source share

Trying to mimic a pattern matching style in C ++ using RTTI is a neat idea, but it has flaws because there are some significant differences between constructors like Haskell and Standard ML and C ++ subclasses. (Note: I am using standard ML syntax below because I am more comfortable with it.)

  • In Haskell and standard ML, pattern matching can bind nested values ​​to pattern variables for you (for example, a::b::c::ds pattern associates the first three elements of a list with a , b and c , and the rest of the list is ds ). In C ++, you still have to dig into the actual nested structures unless you or someone else came up with much more complex macros than suggested here.
  • In Haskell and standard ML, a constructor type type declaration is datatype 'a option = NONE | SOME of 'a datatype 'a option = NONE | SOME of 'a defines one new type: 'a option . The NONE and SOME constructors are not types; they are values ​​with the types 'a option and 'a -> 'a option , respectively. In C ++, when you define subclasses of type Foo and Bar to simulate type constructors, you get new types.
  • In Haskell and Standard ML, SOME type constructors are first-class functions that build the values ​​of the data type to which they belong. For example, map SOME is of type 'a list -> 'a option list . In C ++, using subclasses to model type constructors, you do not get this opportunity.
  • In Haskell and Standard ML, data types are closed, so no one can add more type constructors without changing the original declaration, and the compiler can verify at compile time that a pattern match handles all cases. In C ++, you need to go well to restrict who can subclass your base class.

In the end, do you get enough benefit from modeling simulated patterns compared to using C ++ polymorphism in a more typical way? Is using macros to map simulated patterns a bit more concise (while obfuscating it for anyone reading your code) is worth it?

+5
source share

We have jointly created a pattern matching library for C ++, which allows for very efficient pattern matching and type analysis. A library called Mach7 was released under the BSD license and is available on GitHub: https://github.com/solodon4/Mach7 . You can find videos, posters, slides, documents, as well as source code. It currently supports GCC 4.4+, Clang 3.4+, and Visual C ++ 2010+. Feel free to ask a question about the library by posting a GitHub issue against your repository.

+4
source share

Think that this macro does exactly what you want:

 #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) 

Using:

 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> 

I would not recommend this material in code that should be read by someone else, but since you mentioned the word "macro" ...

In addition, I would not pretend that this has to do with pattern matching in the Haskell / OCaml style. Check out Scala if you want a language that has semantics similar to C ++ (well, sort) and true pattern matching.

+2
source share

All Articles