Can somebody please explain this Rx++ code a bit?

Nov 26, 2012 at 11:19 PM

From rxcpp.h

 

 template 
    struct fix0_thunk {
        F f;
        fix0_thunk(F&& f) : f(std::move(f))
        {
        }
        void operator()() const 
        {
            f(*this);
        }
    };
    template 
    fix0_thunk fix0(F f)
    {
        return fix0_thunk(std::move(f));
    }

then used in:

    DefaultScheduler::Instance().Schedule(
                fix0([=](std::function self) // TODO:
            {
                try {

Thanks.

Nov 27, 2012 at 7:27 AM

Guess we don't need that right now :)

If extended, that thing (fix0_thunk) could be used to store some context, specific to the scheduled task. e.g. a thread handle, some identifier, ...

 

 

Nov 28, 2012 at 2:34 AM
Edited Nov 28, 2012 at 9:07 AM

It's a cool trick, I agree. Another addition to our toolkit.

Jan 21, 2013 at 5:10 PM
Edited Jan 21, 2013 at 7:08 PM

I have advanced a bit in my understanding of the use of fix0 function. It is fixed point combinator for a function that takes no arguments and returns void, i.e. for a work function passed to scheduler's Schedule() call.

The problem that is solves here is defining a recursive function inline. One obvious approach, as shown below, won't work because the reference to the recursive function will go out of scope as soon as enclosing function gets executed. 

std::function<void()> work;

work = [=,&work]() 
{
  try {
          if (state->cancel)
               return;

          if (!state->rem)
          {
                observer->OnCompleted();
          }
          else
          {
                observer->OnNext(state->i);
                --state->rem; 
                ++state->i;
                DefaultScheduler::Instance().Schedule(work);
           }
       } catch (...) {
                observer->OnError(std::current_exception());
       }                
};

DefaultScheduler::Instance().Schedule(work);

Another solution, that will work, is to stuff the function object into the state object. State object persists because it is wrapped in shared pointer.

        return CreateObservable<Integral>(
            [=](std::shared_ptr<Observer<Integral>> observer) -> Disposable
        {
            struct State 
            {
                bool cancel;
                Integral i;
                Integral rem;
                std::function<void()> work;
            };

            auto state = std::make_shared<State>();
            state->cancel = false;
            state->i = start;
            state->rem = (end - start) / step;

            state->work = [=]() 
            {
                try {
                    if (state->cancel)
                        return;

                    if (!state->rem)
                    {
                        observer->OnCompleted();
                    }
                    else
                    {
                        observer->OnNext(state->i);
                        --state->rem; 
                        ++state->i;
                        DefaultScheduler::Instance().Schedule(state->work);
                    }
                } catch (...) {
                    observer->OnError(std::current_exception());
                }                
            };

            DefaultScheduler::Instance().Schedule(state->work);

            return [=]{ state->cancel = true; };
        });
    }

There are reasons (maybe not really applicable to this particular case, but good reasons nonetheless) to avoid this solution with function variable and to use fixed point combinator instead. See Wes Dyer explanation for C# at : http://blogs.msdn.com/b/wesdyer/archive/2007/02/02/anonymous-recursion-in-c.aspx

Of course, I still don't understand what TODO comment next to use of fix0 means. It implies something still needs to be done, but says nothing about what and why.

Jan 21, 2013 at 7:28 PM

hm... yeah, thank you for the details.

It looks I misinterpreted the usage of that fix0 thing.