* []" in the C ++ source code of the cpp.react library Here is the C ++ snippet I found in the cpp.react library documentation : auto...">

Strange "-> * []" in the C ++ source code of the cpp.react library

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

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 it was just a typo, but I found an expression like this 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)? What does it mean?

+53
c ++ language-lawyer c ++ 11 operator-arrow-star
May 12 '14 at 9:27
source share
3 answers

The only example on the linked page where I see ->* is:

 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, my hunch is that any type returned by D::MakeVar() overloads the operator pointer to a member ->* , and the second argument to this overloaded operator is a function object, i.e. lambda expression.

Like in this example:

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

I assume that any types of width , height and depth overload the operator with a comma, and the result gives the same type as for MakeVar , or another type that overloads ->* . The rest is the same as in the first example.

+42
May 12 '14 at 21:41
source share

@Praetorian answer is correct. This is cpp.react code

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

since you can see that it overloads the free function operator->* , which takes a signal ( D::MakeVar(0) ) and a functor (lambda)

and a free operator, function, which takes two signals

+17
May 12 '14 at 22:02
source share

(Author here)

First of all, Praetorians answer correctly, but I would like to clarify a little.

Please note that this library is still very experimental and I'm still working on documentation. The current status of this documentation can be found on the wiki, in particular https://github.com/schlangster/cpp.react/wiki/User-Guide-%7C-Signals related to the question.

Here is a more detailed 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() 

This is a type of binding (signal binding as a function input), but it is NOT the same as the inverse of std :: bind. The volume is not a functional object. In particular, the volume is not recalculated when Value () is called, it is recalculated when one of its dependent signals changes, the result is saved, and Value () returns it. Thus, it significantly promotes the distribution of changes on the basis with some additional functions (without redundant updates, without failures, optional implicit parallelization).

The problem is that MakeSignal gets confused when mixing with temporary signals and 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 such things, right? At least I don't want to.

So there is an alternative syntax that moves the dependencies on the left, wrapped in a 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 an 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 everyone who sees it for the first time does not know what is going on.

On the other hand, using this library when connecting signals to functions should be a very common task. As soon as you know what it is doing, the version β†’ * is more concise and visualizes a graph of the data flow (edges from width and height to time domain, edges from region and depth to volume).

+10
May 15 '14 at 16:57
source share



All Articles