如何在 C++ 中实现类似 Erlang 的发送和接收?

发布于 2024-07-05 22:23:11 字数 1368 浏览 9 评论 0原文

实际上,这个问题似乎有两个部分:

  • 如何实现模式匹配?
  • 如何实现发送和接收(即Actor模型)?

对于模式匹配部分,我一直在研究各种项目,例如 App提案。 这些看起来相当不错,但无法让它们在 g++ 的最新版本 (4.x) 上工作。 Felix 语言似乎也很好地支持模式匹配,但它并不是真正的 C++。

至于 Actor 模型,已有 ACT++ 和 Theron,但除了关于前者的论文之外我找不到任何东西,而后者只是单线程的 [参见答案]。

就我个人而言,我使用线程和线程安全消息队列来实现参与者。 消息是类似散列的结构,并将它们与许多预处理器宏一起使用来实现简单的模式匹配。

现在,我可以使用以下代码发送消息:

(new Message(this))
    ->set("foo", "bar")
    ->set("baz", 123)
    ->send(recipient);

并使用以下代码进行简单的模式匹配(qDebugqPrintable 是 Qt 特定的):

receive_and_match(m)
    match_key("foo")    { qDebug("foo: %s", qPrintable(m->value("foo").toString())); }
    or_match_key("baz") { qDebug("baz: %d", m->value("baz").toInt()); }
    or_match_ignore
end_receive

但是,这看起来对我来说有点老套,而且不是很健壮。

你会怎么做? 我错过了任何现有的工作吗?

Actually, this question seems to have two parts:

  • How to implement pattern matching?
  • How to implement send and receive (i.e. the Actor model)?

For the pattern matching part, I've been looking into various projects like App and Prop. These look pretty nice, but couldn't get them to work on a recent version (4.x) of g++. The Felix language also seems to support pattern matching pretty well, but isn't really C++.

As for the Actor model, there are existing implementations like ACT++ and Theron, but I couldn't find anything but papers on the former, and the latter is single-threaded only [see answers].

Personally, I've implemented actors using threading and a thread-safe message queue. Messages are hash-like structures, and used these together with a number of preprocessor macros to implemented simple pattern matching.

Right now, I can use the following code to send a message:

(new Message(this))
    ->set("foo", "bar")
    ->set("baz", 123)
    ->send(recipient);

And the following to do simple pattern matching (qDebug and qPrintable are Qt-specific):

receive_and_match(m)
    match_key("foo")    { qDebug("foo: %s", qPrintable(m->value("foo").toString())); }
    or_match_key("baz") { qDebug("baz: %d", m->value("baz").toInt()); }
    or_match_ignore
end_receive

However, this looks a bit hackish to me, and isn't very robust.

How would you do it? Did I miss any existing work?

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

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

发布评论

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

评论(7

哎呦我呸! 2024-07-12 22:23:12

对于Actor模型来说,有
现有的实现,例如 ACT++
和塞隆,但我找不到
除了前者的论文之外的任何内容,以及
后者只是单线程的。

作为 Theron 的作者,我很好奇你为什么相信它是单线程的?

就我个人而言,我已经实现了演员
使用线程和线程安全
消息队列

这就是 Theron 的实现方式.. :-)

Ash

As for the Actor model, there are
existing implementations like ACT++
and Theron, but I couldn't find
anything but papers on the former, and
the latter is single-threaded only.

As the author of Theron, I was curious why you believe it's single-threaded?

Personally, I've implemented actors
using threading and a thread-safe
message queue

That's how Theron is implemented.. :-)

Ash

身边 2024-07-12 22:23:12

关于 erlang 的重要事情之一是如何使用这些功能来构建健壮的系统。

发送/接收模型是非共享的,并且是显式复制的。
进程本身是轻量级线程。

如果您确实需要 erlang 模型的强大特性,那么最好使用真实的进程和 IPC 而不是线程。

如果您想要健壮的消息传递,尽管您可能最终想要序列化和反序列化内容。 尤其是类型安全。

C++ 中的模式匹配并不总是很漂亮,但会有一个很好的模式 - 您最终将创建一个调度程序对象,它使用某种形式的多态性来获得您想要的内容。

尽管如果你不小心,你最终会在管道上得到 xml :)

真的,如果你想要 erlang 模型,你真的想使用 erlang。 如果存在速度慢的情况,我相信您可以使用外部功能互联网来增强您的程序。

重新实现部件的问题是您不会获得良好的内聚库和解决方案。 您已经拥有的解决方案看起来不再像 C++。

One of the important things about erlang is how the features are used to make robust systems.

The send/recieve model is no-sharing, and explicitly copying.
The processes themselves are lightweight threads.

If you did desire the robust properties of the erlang model, you would be best to use real processes and IPC rather than threads.

If you want robust message passing though you may end up wanting to serialize and deserialise the contents. Especially with type safety.

Pattern matching in C++ isn't always pretty but there will be a good pattern for this - you will end up creating a dispatcher object that uses some form of polymorphism to get what you want.

Although if you are not careful you end up with xml over pipes :)

Really, if you want the erlang model you really want to use erlang. If there are slow bits, I'm sure you can augment your program using a foreign function internet.

The problem about re-implementing parts, is you won't get a good cohesive library and solution. The solutions you have already don't look much like C++ anymore.

那些过往 2024-07-12 22:23:12

我目前正在为 C++ 实现一个名为“acedia”的 actor 库(谷歌上还没有任何关于它的信息),它使用“类型匹配”。 该库是我的硕士论文的一个项目,您可以用它向演员发送任何类型的数据。

一个小片段:

recipient.send(23, 12.23f);

在接收方,您可以像这样分析收到的消息:

Message msg = receive();
if (msg.match<int, float>() { ... }

...或者您可以定义一个为您调用函数或方法的规则集:

void doSomething(int, float);

InvokeRuleSet irs;
irs.add(on<int, float>() >> doSomething);
receiveAndInvoke(irs);

也可以匹配类型和值:

Message msg = receive();
if (msg.match<int, float>(42, WILDCARD) { ... }
else if (msg.match<int, float>() { ... }

常量“WILDCARD”意味着任何值都将被接受。 不传递任何参数等于将所有参数设置为“WILDCARD”; 这意味着您只想匹配类型。

这当然是一个小片段。 您还可以像 Scala 中一样使用“案例类”。 它们相当于 erlang 中的“原子”。 这是一个更详细的示例:

ACEDIA_DECLARE_CASE_CLASS(ShutdownMessage)
ACEDIA_DECLARE_CASE_CLASS(Event1)
ACEDIA_DECLARE_CASE_CLASS(Event2)

要对定义的案例类做出反应,您可以像这样编写一个演员:

class SomeActor : public Actor
{

  void shutdown() { done = true; }
  void handleEvent1();
  void handleEvent1();

  public:

    SomeActor() : done(false) { }

    virtual void act()
    {
      InvokeRuleSet irs;
      irs
        .add(on<ShutdownMessage>() >> method(&SomeActor::shutdown))
        .add(on<Event1>() >> method(&SomeActor::handleEvent1))
        .add(on<Event2>() >> method(&SomeActor::handleEvent2))
      ;
      while (!done) receiveAndInvoke(irs);
    }

};

要创建一个新的演员并启动它,您所需要编写的是:

Acedia::spawn<SomeActor>();

虽然库甚至没有达到测试体育场,但显示的片段可以工作我有第一个应用程序在上面运行。 该库的主要目标之一是支持分布式编程(也可以跨网络)。

你的问题是不久前提出的,但如果你对此感兴趣:请告诉我! :)

I'm currently implementing an actor library for C++ called "acedia" (there's nothing yet about it on google) that uses "type matching". The library is a project for my master thesis and you can send any kind of data to an actor with it.

A small snippet:

recipient.send(23, 12.23f);

And on the recipient side you can either analyze the received message like this:

Message msg = receive();
if (msg.match<int, float>() { ... }

... or you can define a rule set that invokes a function or method for you:

void doSomething(int, float);

InvokeRuleSet irs;
irs.add(on<int, float>() >> doSomething);
receiveAndInvoke(irs);

It's also possible to match both on type and on value:

Message msg = receive();
if (msg.match<int, float>(42, WILDCARD) { ... }
else if (msg.match<int, float>() { ... }

The constant "WILDCARD" means, that any value will be acceptet. Pass no arguments is equal set all arguments to "WILDCARD"; meaning that you only want to match the types.

This is certainly a small snippet. Also you can use "case classes" like in Scala. They are comparable to "atomics" in erlang. Here is a more detailed example:

ACEDIA_DECLARE_CASE_CLASS(ShutdownMessage)
ACEDIA_DECLARE_CASE_CLASS(Event1)
ACEDIA_DECLARE_CASE_CLASS(Event2)

To react to the defined case classes you can write an actor like this:

class SomeActor : public Actor
{

  void shutdown() { done = true; }
  void handleEvent1();
  void handleEvent1();

  public:

    SomeActor() : done(false) { }

    virtual void act()
    {
      InvokeRuleSet irs;
      irs
        .add(on<ShutdownMessage>() >> method(&SomeActor::shutdown))
        .add(on<Event1>() >> method(&SomeActor::handleEvent1))
        .add(on<Event2>() >> method(&SomeActor::handleEvent2))
      ;
      while (!done) receiveAndInvoke(irs);
    }

};

To create a new actor and start it, all you have to write is:

Acedia::spawn<SomeActor>();

Although the library not even reached beta stadium the shown snippets work and i have a first application running on it. One major goal of the library is to support distributed programming (also across a network).

Your question is a while ago, but if you're interested in it: let me know! :)

够运 2024-07-12 22:23:12

我肯定有兴趣查看您的“acedia”图书馆,并愿意尽我所能提供帮助。 Erlang 有一些很棒的构造,C++ 绝对可以从这样的库中受益。

I would definitely be interested in looking at your "acedia" library and would love to help in any way that I could. Erlang has some wonderful constructs and C++ could definitely benefit from such a library.

樱&纷飞 2024-07-12 22:23:12

您可以使用 Qt 的信号/槽机制来模拟行为,特别是因为 Qt 的信号/槽支持多线程。

You can mimic the behavior using Qt's signal/slot mechanism, especially since Qt's signal/slot supports multithread.

不再见 2024-07-12 22:23:12

今天我在 sourceforge 托管该库:https://sourceforge.net/projects/acedia/

我之前说过这是一个早期版本。 但请随意批评它!

Today I hostet the library at sourceforge: https://sourceforge.net/projects/acedia/

As I said before it's an early release. But feel free to critique it!

时间海 2024-07-12 22:23:12

今天,如果你想要 C++ 中的 erlang 风格的健壮的 actor 和模式匹配,
也许 Rust 就是答案。

当然,当 OP 约 5 年前询问时,这还没有公开,截至 2014 年 4 月,它仍然不是 v1.0 - 但它进展得很好并且绝对稳定,足够的语言核心是稳定的我思考。

好吧,它不是 C++,但它具有与 C++ 相同的内存管理方法,除了它默认支持没有共享内存的轻量级任务(然后提供用于共享的受控库功能 - “Arc”);
它可以直接调用(并直接公开)“extern C”函数。 您不能与 C++ 共享模板化库标头 - 但您可以编写模仿 C++ 集合类的泛型(反之亦然)来传递对数据结构的引用。

Today, if you want erlang style robust actors in C++, and pattern matching,
maybe Rust is the answer.

Of course this wasn't around publically when the OP asked ~5years ago, and as of april 2014 it still isn't v1.0 yet - but its been progressing very well and is definitely stabilizing, enough of the language core is stable I think.

And ok its not C++, but it has the same approach to memory management as C++, except that it supports lightweight tasks with no shared memory by default (then provides controlled library features for sharing - "Arc");
It can directly call (and directly expose) 'extern C' functions. You can't share templated library headers with C++ - but you can write generics that mimick C++ collection classes (and vica versa) to pass references to data-structures across.

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