55 votes

Expression étrange "-> * []" dans le code source C ++ de la bibliothèque cpp.react

Ici, c'est un C++ extrait que j'ai trouvé dans la documentation de la rpc.réagir de la bibliothèque:

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

Je n'ai jamais vu l' ->* [] de la notation. Tout d'abord, j'ai pensé que c'était juste une faute de frappe, mais j'ai aussi trouvé une expression dans le code source:

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

Est-ce valable en C++11 (ou C++14)? Ça veut dire quoi?

42voto

Praetorian Points 47122

Le seul exemple sur la page du lien que je vois ->* est-ce:

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;
};

Voici ma conjecture - quel que soit le type retourné par D::MakeVar() surcharge le pointeur-à-membres de l'opérateur ->*, et le deuxième argument pour que la surcharge de l'opérateur est une fonction de l'objet, c'est à dire l'expression lambda.

Comme pour cet exemple:

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

Je devine quels que soient les types d' width, height & depth sont, la surcharge de l'opérateur virgule, et le résultat donne le même type que ce que l' MakeVar rendements, ou d'un autre type que les surcharges ->*. Le reste est le même que le premier exemple.

17voto

Bryan Chen Points 14689

La réponse de Praetorian est correcte. C'est le code de 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);
}
 

comme vous pouvez le voir, la fonction libre surchargée operator->* prend un signal ( D::MakeVar(0) ) et un functor (lambda)

et fonction libre operator, qui prend deux signaux

10voto

user3641432 Points 81

(Auteur)

Tout d'abord, les Prétoriens réponse est correcte, mais j'aimerais développer un peu.

Notez que cette bibliothèque est encore très expérimental et je travaille encore sur la documentation. L'état actuel de la dite documentation peut être trouvé dans le wiki, en particulier https://github.com/schlangster/cpp.react/wiki/User-Guide-%7C-Signals est liée à la question.

Voici un autre exemple détaillé:

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

C'est une sorte de bind (bind signaux d'entrée de la fonction), mais ce n'est PAS la même que d'une inversion de std::bind. le volume n'est pas un objet de fonction. En particulier, le volume n'est pas recalculée lorsque vous appelez la Valeur(), il est recalculé lorsque l'un de ses signaux dépendants de change, le résultat est enregistré, et la Valeur() renvoie. C'est donc essentiellement pousser le changement de propagation avec quelques fonctionnalités supplémentaires (non redondant mises à jour, pas de problèmes, en option implicite parallélisation).

Le problème est que MakeSignal devient source de confusion lorsqu'il est mélangé avec temporaire des signaux et des 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);

Personne ne veut lire des trucs comme ça, non? Au moins, je ne le veux pas.

Donc, il y a une syntaxe alternative qui déplace les dépendances vers la gauche, enveloppé par 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; });

Et, enfin, avec le mal virgule et ->* surcharges:

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

Le problème avec cela, comme d'autres l'ont noté, c'est que quiconque voit pour la première fois il ne sait pas ce que le diable est en cours.

D'autre part, la connexion de signaux à des fonctions devrait être une tâche très courante lors de l'utilisation de cette bibliothèque. Une fois que vous savez ce qu'il fait, l' ->* la version la plus concise et il permet de visualiser le graphe de flux de données (sur les bords de la largeur et de la hauteur de la zone temporaire, les bords de la zone et la profondeur du volume).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X