Подтвердить что ты не робот

Strange "-> * []" в исходном коде С++ библиотеки cpp.react

Вот фрагмент С++, который я нашел в документации библиотеки cpp.react:

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

Я никогда не видел обозначения ->* []. Во-первых, я думал, что это просто опечатка, но я нашел такое выражение в исходном коде:

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

Является ли это допустимым С++ 11 (или С++ 14)? Что это значит?

4b9b3361

Ответ 1

Единственный пример на связанной странице, где я вижу ->*, это:

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

Здесь моя догадка - любой тип, возвращаемый D::MakeVar(), перегружает оператор указатель на член ->*, а второй аргумент для этого перегруженного оператора объект функции, т.е. лямбда-выражение.

Как в этом примере:

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

Я предполагаю, что любые типы width, height и depth перегружают оператор запятой, а результат дает тот же тип, что и для MakeVar, или другой тип, который перегружает ->*. Остальное - это то же, что и в первом примере.

Ответ 2

@Praetorian answer является правильным. Это - код 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);
}

поскольку вы можете видеть, что он перегружает свободную функцию operator->*, которая принимает сигнал (D::MakeVar(0)) и функтор (лямбда)

и свободная функция operator,, которая принимает два сигнала

Ответ 3

(Автор здесь)

Прежде всего, преторианцы отвечают правильно, но я хотел бы немного уточнить.

Обратите внимание, что эта библиотека все еще очень экспериментальная, и я все еще работаю над документацией. Текущее состояние указанной документации можно найти в вики, в частности 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. том не является функциональным объектом. В частности, объем не пересчитывается при вызове Value(), он пересчитывается, когда изменяется один из его зависимых сигналов, результат сохраняется, а Value() возвращает его. Таким образом, он существенно продвигает распространение изменений на основе с некоторыми дополнительными функциями (без избыточных обновлений, без сбоев, необязательной неявной распараллеливания).

Проблема заключается в том, что 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);

Никто не хочет читать такие вещи, верно? По крайней мере, я не хочу.

Итак, есть альтернативный синтаксис, который перемещает зависимости слева, обернутые 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; };

Проблема с этим, как отмечали другие, заключается в том, что каждый, кто впервые видит это, не знает, что происходит.

С другой стороны, при использовании этой библиотеки при подключении сигналов к функциям должна быть очень распространенная задача. Как только вы знаете, что он делает, версия → * более кратка и визуализирует график потока данных (края от ширины и высоты до временной области, края от области и глубины до тома).