(作者在这里)
首先,Praetorians 的回答是正确的,但我想详细说明一下。
请注意,这个库仍然是非常实验性的,我仍在处理文档。可以在 wiki 中找到所述文档的当前状态,特别是https://github.com/schlangster/cpp.react/wiki/User-Guide-%7C-Signals与该问题有关。
这是一个更详细的示例:
int calcVolume(int w, int h, int d) { return w*h*d; }
D::VarSignalT<int> width = D::MakeVar(1);
D::VarSignalT<int> height = D::MakeVar(2);
D::VarSignalT<int> depth = D::MakeVar(3);
D::SignalT<int> volume = MakeSignal(&calcVolume, width, height, depth);
Observe(volume, [] (int v) {
printf("volume changed to %d\n", v);
});
width.Set(10); // => volume changed to 60.
printf("volume: %d\n", volume.Value()); // short: volume()
这是一种绑定(绑定信号作为函数输入),但它与反向 std::bind 不同。volume 不是函数对象。特别是,当您调用 Value() 时,不会重新计算交易量,而是在其相关信号之一发生变化时重新计算交易量,结果被保存,并且 Value() 将其返回。所以它本质上是基于推送的更改传播,带有一些额外的功能(没有冗余更新、没有故障、可选的隐式并行化)。
问题是 MakeSignal 在与临时信号和 lambda 混合时会变得混乱:
// First create a temporary area signal, then use it as an argument for the volume signal
D::SignalT<int> volume = MakeSignal(
[] (int a, int d) { return a * d; },
MakeSignal(
[] (int w, int h) { return w * h; },
width, height),
depth);
没有人愿意读这样的东西,对吧?至少我不想。
因此,有一种替代语法将依赖项移到左侧,由 SignalList 包装。
// Note: Not sure if I have already pushed this variant yet
D::SignalT<int> volume =
MakeSignalList(
MakeSignalList(width, height).Bind([] (int w, int h) { return w * h; }),
depth
).Bind([] (int a, int d) { return a * d; });
最后,使用邪恶的逗号和 ->* 重载:
D::SignalT<int> volume =
(
(width, height) ->* [] (int w, int h) { return w * h; },
depth
)
->* [] (int area, int d) { return a * d; };
正如其他人所指出的那样,问题在于任何第一次看到它的人都不知道到底发生了什么。
另一方面,在使用这个库时,将信号连接到函数应该是一项非常常见的任务。一旦你知道它做了什么, ->* 版本会更加简洁,它可以可视化数据流图(从宽度和高度到临时区域的边缘,从区域和深度到体积的边缘)。