简体   繁体   English

在cpp.react库的C ++源代码中有奇怪的“ - > * []”表达式

[英]Strange “->* []” expression in C++ source code of cpp.react library

Here is a C++ snippet that I found in the documentation of the cpp.react library : 这是我在cpp.react库的文档中找到的C ++代码段:

auto in = D::MakeVar(0);
auto op1 = in ->* [] (int in)
{
    int result = in /* Costly operation #1 */;
    return result;
};

I have never seen the ->* [] notation. 我从未见过->* []表示法。 First, I thought that it was just a typo, but I also found such an expression in the source code : 首先,我认为这只是一个错字,但我也在源代码中找到了这样一个表达式:

auto volume = (width,height,depth) ->* [] (int w, int h, int d) {
    return w * h * d;
};

Is this valid C++11 (or C++14)? 这是有效的C ++ 11(或C ++ 14)吗? What does it mean? 这是什么意思?

The only example on the linked page where I see ->* is this: 链接页面上我看到的唯一示例->*是这样的:

auto in = D::MakeVar(0);

auto op1 = in ->* [] (int in)
{
    int result = in /* Costly operation #1 */;
    return result;
};

auto op2 = in ->* [] (int in)
{
    int result = in /* Costly operation #2 */;
    return result;
};

Here's my guess - whatever type is returned by D::MakeVar() overloads the pointer-to-member operator ->* , and the second argument for that overloaded operator is a function object, ie the lambda expression. 这是我的猜测 - D::MakeVar()返回的任何类型都会重载指向成员的运算符 ->* ,并且该重载运算符的第二个参数是一个函数对象,即lambda表达式。

As for this example: 至于这个例子:

auto volume = (width,height,depth) ->* [] (int w, int h, int d) {
    return w * h * d;
};

I'm guessing whatever types width , height & depth are, overload the comma operator, and the result yields the same type as what MakeVar yields, or another type that overloads ->* . 我猜测widthheightdepth是什么类型,重载逗号运算符,结果产生与MakeVar产生的类型相同的类型,或者另一种重载类型->* The rest is the same as the first example. 其余与第一个例子相同。

@Praetorian's answer is correct. @Praetorian的回答是正确的。 This is code from cpp.react 是来自cpp.react的代码

///////////////////////////////////////////////////////////////////////////////////////////////////
/// operator->* overload to connect inputs to a function and return the resulting node.
///////////////////////////////////////////////////////////////////////////////////////////////////
// Single input
template
<
    typename D,
    typename F,
    template <typename D_, typename V_> class TSignal,
    typename TValue,
    class = std::enable_if<
        IsSignal<TSignal<D,TValue>>::value>::type
>
auto operator->*(const TSignal<D,TValue>& inputNode, F&& func)
    -> Signal<D, typename std::result_of<F(TValue)>::type>
{
    return D::MakeSignal(std::forward<F>(func), inputNode);
}

// Multiple inputs
template
<
    typename D,
    typename F,
    typename ... TSignals
>
auto operator->*(const InputPack<D,TSignals ...>& inputPack, F&& func)
    -> Signal<D, typename std::result_of<F(TSignals ...)>::type>
{
    return apply(
        REACT_IMPL::ApplyHelper<D, F&&, TSignals ...>::MakeSignal,
        std::tuple_cat(std::forward_as_tuple(std::forward<F>(func)), inputPack.Data));
}

///////////////////////////////////////////////////////////////////////////////////////////////////
/// Comma operator overload to create input pack from 2 signals.
///////////////////////////////////////////////////////////////////////////////////////////////////
template
<
    typename D,
    typename TLeftVal,
    typename TRightVal
>
auto operator,(const Signal<D,TLeftVal>& a, const Signal<D,TRightVal>& b)
    -> InputPack<D,TLeftVal, TRightVal>
{
    return InputPack<D, TLeftVal, TRightVal>(a, b);
}

///////////////////////////////////////////////////////////////////////////////////////////////////
/// Comma operator overload to append node to existing input pack.
///////////////////////////////////////////////////////////////////////////////////////////////////
template
<
    typename D,
    typename ... TCurValues,
    typename TAppendValue
>
auto operator,(const InputPack<D, TCurValues ...>& cur, const Signal<D,TAppendValue>& append)
    -> InputPack<D,TCurValues ... , TAppendValue>
{
    return InputPack<D, TCurValues ... , TAppendValue>(cur, append);
}

as you can see it overloaded free function operator->* which takes a signal ( D::MakeVar(0) ) and a functor (lambda) 正如你可以看到它重载了自由函数operator->*它接受一个信号( D::MakeVar(0) )和一个D::MakeVar(0)函数(lambda)

and free function operator, which takes two signal 和自由函数operator,它接收两个信号

(Author here) (作者在这里)

First of all, Praetorians answer is correct, but I'd like to elaborate a bit. 首先,Praetorians回答是正确的,但我想详细说明一下。

Note that this library is still very experimental and I'm still working on the documentation. 请注意,这个库仍然是非常实验性的,我仍在研究文档。 The current state of said documentation can be found in the wiki, in particular https://github.com/schlangster/cpp.react/wiki/User-Guide-%7C-Signals is related to the question. 所述文档的当前状态可以在wiki中找到,特别是https://github.com/schlangster/cpp.react/wiki/User-Guide-%7C-Signals与该问题相关。

Here's a more verbose example: 这是一个更冗长的例子:

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()

It's sort of a bind (bind signals as function input), but it's NOT the same as a reverse std::bind. 它是一种绑定(绑定信号作为函数输入),但它与反向std :: bind不同。 volume is not a function object. volume不是函数对象。 In particular, volume is not recalculated when you call Value(), it is recalculated when one of its dependent signals changes, the result is saved, and Value() returns it. 特别是,当您调用Value()时,不会重新计算音量,当其中一个相关信号发生更改时,将重新计算音量,保存结果,并返回Value()。 So it's essentially push based change propagation with some extra features (no redundant updates, no glitches, optional implicit parallelization). 因此,它本质上是基于推送的变更传播,具有一些额外的功能(没有冗余更新,没有毛刺,可选的隐式并行化)。

The problem is that MakeSignal gets confusing when mixed with temporary signals and lambdas: 问题是MakeSignal在与临时信号和lambdas混合时会变得混乱:

// 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);

Nobody wants to read stuff like that, right? 没有人想读这样的东西,对吧? At least I don't want to. 至少我不想。

So there's an alternative syntax that moves the dependencies to the left, wrapped by SignalList. 因此,有一种替代语法将依赖项移动到左侧,由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; });

And, finally, with the evil comma and ->* overloads: 最后,用邪恶的逗号和 - > *重载:

D::SignalT<int> volume =
(
    (width, height) ->* [] (int w, int h) { return w * h; },
    depth
)
->* [] (int area, int d) { return a * d; };

The problem with this, as others have noted, is that anyone seeing it for the first time doesn't know what the heck is going on. 正如其他人所指出的那样,问题在于,第一次看到它的人都不知道到底发生了什么。

On the other hand, connecting signals to functions should be a very common task when using this library. 另一方面,在使用此库时,将信号连接到函数应该是一项非常常见的任务。 Once you know what it does, the ->* version is more concise and it visualizes the dataflow graph (edges from width and height to the temporary area, edges from area and depth to volume). 一旦你知道它做了什么, - > *版本更简洁,它可视化数据流图(从宽度和高度到临时区域的边缘,从区域和深度到体积的边缘)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM