C++ 中的模式匹配风格?

发布于 2024-08-20 17:24:02 字数 398 浏览 10 评论 0原文

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

现在,我可以定义任何宏来简化这个过程吗?我已经思考这个问题有一段时间了,但无法进一步简化。

谢谢!

I love Haskell style pattern matching.

I have my 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 define to simplify this? I have been pondering this for a while, but can't simplify it further.

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

情深如许 2024-08-27 17:24:02

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

dynamic_cast would appear 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";
}
赤濁 2024-08-27 17:24:02

我喜欢 Haskell 风格的模式匹配。

然后用 Haskell 编写你的程序。

您要做的是切换类型。如果人们想避免虚拟函数,这是一种常见的做法。现在,后者是 C++ 中的 OO 的基石。如果你想避免它们,为什么要用 C++ 编程呢?


至于为什么这会让人皱眉:想象一下,您的代码中有很多这样的代码

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

,然后有人来将 Baz 添加到 ptr 可能的类型中代表。现在,您正在搜索大型代码库,尝试找到所有切换类型的位置,并尝试找出需要添加 Baz 的位置。

而且,正如 Murphy 所说,当您完成后,Foz 也会作为类型添加。 (或者,再想一想,如果墨菲按照他的方式行事,那么它就会在你有机会完全添加 Baz 之前就悄悄出现。)

I love Haskell style pattern matching.

Then write your program in Haskell.

What you're trying to do is a switch over a type. That's a common thing people do if they want to avoid virtual functions. Now, the latter are a cornerstone of what OO in C++ is all about. If you want to avoid them, why do you program in C++?


As for why this is frowned upon: 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 and adds Baz to the possible types that ptr might represent. Now you're hunting through a big code base, trying to find all those places where you switched over a type, and trying to find out which ones you need to add Baz to.

And, as Murphy has it, just when your done, there comes along Foz to be added as a type, too. (Or, thinking again, if Murphy has his way it creeps in before you had a chance too complete adding Baz.)

暖伴 2024-08-27 17:24:02

尝试使用 RTTI 在 C++ 中模拟模式匹配样式是一个好主意,但它肯定有缺点,因为 Haskell 和标准 ML 样式类型构造函数和 C++ 子类之间存在一些显着差异。 (注意:下面,我使用标准 ML 语法,因为我更习惯它。)

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

最后,与以更典型的方式使用 C++ 多态性相比,您是否从模拟模式匹配中获得了足够的好处?使用宏使模拟模式匹配稍微更简洁(同时对阅读您代码的其他人进行混淆)是否值得?

Attempting to simulate a pattern matching style in C++ using RTTI is a neat idea, but it's bound to have shortcomings, because there are some significant differences between Haskell and Standard ML style type constructors and C++ subclasses. (Note: below, I use Standard ML syntax because I'm more comfortable with it.)

  • In Haskell and Standard ML, pattern matching can bind nested values to pattern variables for you (e.g. the pattern a::b::c::ds binds the first three elements of the list to a, b, and c, and the rest of the list to ds). In C++, you'll still have to dig around in the actual nested structures, unless you or someone else comes up with far more complicated macros than have been proposed here.
  • In Haskell and Standard ML, a type constructor datatype declaration like datatype 'a option = NONE | SOME of 'a defines one new type: 'a option. Constructors NONE and SOME are not types, they are values with types 'a option and 'a -> 'a option, respectively. In C++, when you define subclasses like Foo and Bar to simulate type constructors, you get new types.
  • In Haskell and Standard ML, constructors like SOME are first-class functions that construct values of the datatype to which they belong. For example, map SOME has the type 'a list -> 'a option list. In C++, using subclasses to simulate type constructors, you don't get this ability.
  • In Haskell and Standard ML, datatypes are closed, so no one can add more type constructors without changing the original declaration, and the compiler can verify at compile time that the pattern match handles all cases. In C++, you have to go well out of your way to restrict who can subclass your base class.

In the end, are you getting enough benefit from simulated pattern matching compared to using C++ polymorphism in a more typical way? Is using macros to make simulated pattern matching slightly more concise (while obfuscating it for everyone else who reads your code) worthwhile?

装迷糊 2024-08-27 17:24:02

我们共同创作了一个 C++ 模式匹配库,它允许您非常有效地进行模式匹配和类型分析。该库名为 Mach7,已在 BSD 许可下发布,可在 GitHub 上获取:https://github.com/索洛登4/马赫7。您可以在那里找到视频、海报、幻灯片、论文以及源代码。它目前支持 GCC 4.4+、Clang 3.4+ 和 Visual C++ 2010+。欢迎通过针对其存储库提交 GitHub 问题来询问有关该库的问题。

We co-authored a pattern matching library for C++ that allows you to do pattern matching and type analysis very efficiently. The library, called Mach7, has been released under BSD license and is available on GitHub: https://github.com/solodon4/Mach7. You can find videos, posters, slides, papers as well as the source code there. It currently supports GCC 4.4+, Clang 3.4+ and Visual C++ 2010+. Feel free to ask question's about the library by submitting a GitHub issue against its repository.

清风疏影 2024-08-27 17:24:02

我假设您的 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++ 中做错了什么。您应该考虑使用虚函数。

I'm assuming that 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>()) {
  ......
}

Though, as others have noted, switching on type is usually a sign you're doing something wrong in C++. You ought to consider using virtual functions instead.

哥,最终变帅啦 2024-08-27 17:24:02

我认为这个宏正是您想要的:

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

A think this macro does precisely 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)                                           

Usage:

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 wouldn't recommend this sort of stuff in code that is meant to be read by somebody else, but since you mentioned the word "macro"...

Also, I wouldn't pretend this has anything to do with pattern matching in the Haskell/OCaml style. Check Scala if you want a language that has semantics similar to C++ (well, sort of) and true pattern matching.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文