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

前端 未结 3 1496
一向
一向 2021-01-30 20:09

Here is a C++ snippet that I found in the documentation of the cpp.react library:

auto in = D::MakeVar(0);
auto op1 = in ->* [] (int in)
{
    int result = in         


        
3条回答
  •  -上瘾入骨i
    2021-01-30 20:53

    (Author here)

    First of all, Praetorians answer is correct, but I'd like to elaborate a bit.

    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.

    Here's a more verbose example:

    int calcVolume(int w, int h, int d) { return w*h*d; }
    
    D::VarSignalT width  = D::MakeVar(1);
    D::VarSignalT height = D::MakeVar(2);
    D::VarSignalT depth  = D::MakeVar(3);
    
    D::SignalT 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. volume is not a function object. 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. 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:

    // First create a temporary area signal, then use it as an argument for the volume signal
    D::SignalT 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.

    // Note: Not sure if I have already pushed this variant yet
    D::SignalT 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 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).

提交回复
热议问题