我们将如何在C ++中实现类似于Erlang的发送和接收?
实际上,这个问题似乎包含两个部分:
- 如何实现模式匹配?
- 如何实现发送和接收(即Actor模型)?
对于模式匹配部分,我一直在研究诸如App和Prop之类的各种项目,它们看起来不错,但无法使它们在g ++的最新版本(4.x)中工作。 Felix语言似乎也很好地支持模式匹配,但实际上不是C ++。
至于Actor模型,有诸如ACT ++和Theron之类的现有实现,但是除了关于前者的论文外,我什么也找不到,而后者仅是单线程的[请参见答案]。
就个人而言,我已经使用线程和线程安全消息队列实现了参与者。消息是类似散列的结构,并将它们与许多预处理器宏一起使用以实现简单的模式匹配。
现在,我可以使用以下代码发送消息:
(new Message(this)) ->set("foo", "bar") ->set("baz", 123) ->send(recipient);
然后执行简单的模式匹配(qDebug和qPrintable是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
但是,对于我来说,这似乎有点不足,而且不是很可靠。
你会怎么做?我错过任何现有的工作吗?
解决方案
回答
关于erlang的重要事情之一是如何使用这些功能来构建强大的系统。
发送/接收模型是不共享的,并且是显式复制的。
进程本身是轻量级线程。
如果我们确实希望erlang模型具有强大的属性,那么最好使用实际的进程和IPC,而不是线程。
如果我们希望传递健壮的消息,则可能最终想要序列化和反序列化内容。特别是具有类型安全性。
C ++中的模式匹配并不总是很漂亮,但是会有一个很好的模式,我们最终将创建一个使用某种形式的多态性获得所需内容的调度程序对象。
虽然如果不小心,最终会在管道上出现xml :)
真的,如果我们要使用erlang模型,那么我们真的想使用erlang。如果速度较慢,我相信我们可以使用外部功能的互联网来扩展程序。
重新实现零件的问题是,我们将不会获得良好的内聚库和解决方案。我们已经拥有的解决方案看起来不再像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.
作为Theron的作者,我很好奇我们为什么认为它是单线程的?
Personally, I've implemented actors using threading and a thread-safe message queue
那就是Theron的实现方式.. :-)
灰
回答
我当前正在为C ++实现一个演员库,该演员库使用"类型匹配",名为" acedia"(在Google上还没有此库)。该库是我的硕士论文的一个项目,我们可以使用它将任何类型的数据发送给演员。
一个小片段:
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中的" atomics"。这是一个更详细的示例:
ACEDIA_DECLARE_CASE_CLASS(ShutdownMessage) ACEDIA_DECLARE_CASE_CLASS(Event1) ACEDIA_DECLARE_CASE_CLASS(Event2)
要对已定义的案例类做出反应,我们可以编写如下的actor:
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); } };
要创建一个新的actor并启动它,我们需要编写的就是:
Acedia::spawn<SomeActor>();
尽管图书馆甚至还没有到达Beta体育场,但显示的摘录仍然有效,我在上面运行了第一个应用程序。该库的一个主要目标是支持分布式编程(也可以跨网络)。
问题是前一阵子,但是如果我们对此感兴趣,请告诉我! :)
回答
我肯定会对" acedia"库感兴趣,并希望以我能提供的任何方式提供帮助。 Erlang有一些很棒的构造,C ++肯定可以从这样的库中受益。
回答
今天,我在sourceforge托管了该库:https://sourceforge.net/projects/acedia/
如我之前所说,它是早期版本。但是随时批评吧!
回答
我们可以使用Qt的信号/插槽机制模拟行为,尤其是因为Qt的信号/插槽支持多线程。