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

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

How would you implement Erlang-like send and receive in C++?

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

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

对于模式匹配部分,我一直在研究诸如App和Prop之类的各种项目,它们看起来不错,但无法使它们在g ++的最新版本(4.x)中工作。 Felix语言似乎也很好地支持模式匹配,但实际上不是C ++。

至于Actor模型,有诸如ACT ++和Theron之类的现有实现,但是除了前者的论文外,我什么都找不到,而后者仅是单线程的 [请参阅答案]。

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

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

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

并执行以下简单模式匹配(qDebugqPrintable是Qt特定的):

1
2
3
4
5
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

但是,对我来说,这似乎有点不足,而且不够可靠。

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


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.

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

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

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


我目前正在为C ++实现一个演员库,该演员库使用"类型匹配",名为" acedia"(在Google上还没有此库)。该库是我的硕士论文的一个项目,您可以使用它将任何类型的数据发送给演员。

一个小片段:

1
recipient.send(23, 12.23f);

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

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

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

1
2
3
4
5
void doSomething(int, float);

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

还可以同时匹配类型和值:

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

常量" WILDCARD"表示将接受任何值。不传递任何参数等于将所有参数设置为" WILDCARD";表示您只想匹配类型。

这当然是一个小片段。您也可以像在Scala中那样使用"案例类"。它们相当于erlang中的" atomics"。这是一个更详细的示例:

1
2
3
ACEDIA_DECLARE_CASE_CLASS(ShutdownMessage)
ACEDIA_DECLARE_CASE_CLASS(Event1)
ACEDIA_DECLARE_CASE_CLASS(Event2)

要对已定义的案例类做出反应,您可以编写如下的actor:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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);
    }

};

要创建一个新角色并启动它,您只需编写以下内容:

1
Acedia::spawn<SomeActor>();

尽管图书馆甚至还没有到达Beta体育场,但显示的摘录仍然有效,并且我在其上运行了第一个应用程序。该库的一个主要目标是支持分布式编程(也可以跨网络)。

您的问题是前一阵子,但是如果您对此感兴趣,请告诉我! :)


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

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

如果您确实希望erlang模型具有强大的属性,那么最好使用实际的进程和IPC,而不是线程。

如果您希望传递健壮的消息,则可能最终想要序列化和反序列化内容。特别是具有类型安全性。

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

尽管如果不小心,最终会在管道上出现xml :)

确实,如果您想要erlang模型,那么您确实要使用erlang。如果速度较慢,我相信您可以使用外部功能的互联网来扩展程序。

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


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


如今,如果您希望使用C ++的erlang样式健壮的actor和模式匹配,
也许Rust是答案。

当然,当OP大约5年前提出要求时,这还不是公开的,到2014年4月,它仍然不是v1.0-但是它进展得非常好,而且确实很稳定,足够的语言核心是稳定的我认为。

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


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

如我之前所说,它是早期版本。但是随时批评吧!


我肯定会对您的" acedia"库感兴趣,并希望以我能提供的任何方式提供帮助。 Erlang有一些很棒的构造,C ++肯定可以从这样的库中受益。


推荐阅读