繁体   English   中英

C ++中的模式匹配样式?

[英]Pattern matching style in C++?

我喜欢Haskell样式模式匹配。

我的C ++代码如下:

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

现在,我可以定义一些宏来简化此过程吗? 我已经考虑了一段时间,但无法进一步简化它。

谢谢!

dynamic_cast似乎dynamic_cast您的要求

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";
}

我喜欢Haskell样式模式匹配。

然后在Haskell中编写程序。

您想要做的是切换类型。 如果人们想避免使用虚函数,那是很常见的事情。 现在,后者是C ++中OO的全部基石。 如果要避免它们,为什么要用C ++编程?


至于为什么对此不满意:想象您有很多这样的代码

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

在您的代码中涂抹所有内容,然后有人来将Baz添加到ptr可能表示的可能类型中。 现在,您正在遍历庞大的代码库,试图找到所有您在其中切换了类型的地方,并试图找出需要向其添加Baz的地方。

而且,正如墨菲(Furphy)所做的那样,当您完成操作时,也会附带Foz作为类型添加。 (或者,再三考虑,如果墨菲有他的方法,那么在您没有完全添加Baz的机会之前,它就会悄悄Baz 。)

尝试使用RTTI在C ++中模拟模式匹配样式是一个好主意,但它必然会有缺点,因为Haskell和Standard ML样式类型构造函数与C ++子类之间存在一些显着差异。 (注意:下面,我使用标准ML语法,因为我对此比较满意。)

  • 在Haskell和Standard ML中,模式匹配可以为您将嵌套值绑定到模式变量(例如,模式a::b::c::ds将列表的前三个元素绑定到abc ,其余的列表中的ds )。 在C ++中,您仍然必须深入研究实际的嵌套结构,除非您或其他人提出了比此处提出的复杂得多的宏。
  • 在Haskell和Standard ML中,类型构造函数数据类型声明(如datatype 'a option = NONE | SOME of 'a datatype 'a option = NONE | SOME of 'a定义了一种新类型: 'a option 构造函数NONESOME不是类型,它们分别是类型为'a option'a -> 'a option 在C ++中,当您定义诸如FooBar类的子类来模拟类型构造函数时,您将获得新的类型。
  • 在Haskell和Standard ML中,像SOME这样的构造函数是一类函数,它们构造它们所属的数据类型的值。 例如, map SOME具有类型'a list -> 'a option list 在C ++中,使用子类来模拟类型构造函数,您不会获得此功能。
  • 在Haskell和Standard ML中,数据类型是封闭的,因此没有人可以在不更改原始声明的情况下添加更多类型构造函数,并且编译器可以在编译时验证模式匹配是否可以处理所有情况。 在C ++中,您必须尽力限制谁可以继承您的基类。

最后,与以更典型的方式使用C ++多态相比,您是否从模拟模式匹配中获得了足够的收益? 是否值得使用宏使模拟模式匹配更简洁(同时使其他读取您的代码的人感到困惑)是否值得?

我们与他人合着了C ++模式匹配库,使您可以非常高效地进行模式匹配和类型分析。 名为Mach7的库已根据BSD许可发布,可在GitHub上找到: https : //github.com/solodon4/Mach7 您可以在此处找到视频,海报,幻灯片,论文以及源代码。 目前,它支持GCC 4.4 +,Clang 3.4+和Visual C ++ 2010+。 通过针对其存储库提交GitHub问题,随时提出有关该库的问题。

我假设您的Ptr模板具有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>()) {
  ......
}

但是,正如其他人指出的那样,打开类型通常是在C ++中做错事情的标志。 您应该考虑使用虚函数。

一个想法,这个宏正是您想要的:

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

用法:

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>

我不会在代码中推荐这类东西,以免被其他人阅读,但是由于您提到了“宏”一词,所以……

另外,我不会假装这与Haskell / OCaml样式中的模式匹配有关。 如果您想要一种语言,其语义类似于C ++(很好,有点)并且是真正的模式匹配,请检查Scala。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM