Animations on lambdas in C ++ 11



    Development companies, as a rule, are not in a hurry to switch to the new C ++. Mainly due to the support of its compilers, or rather its complete or partial absence. I recently decided to find out what’s new in terms of supporting C ++ 11 with the GCC compiler, and I realized that it’s time to start . Fortunately, we at Ivideon are loyal to new technologies and give us the opportunity to try something new.
    I started, of course, with the most delicious - with lambda expressions! And with threads.


    I want to present to the reader a small sketch on the topic of how lambdas can help make the code more readable and concise. Such code can be used wherever you need to call a function periodically, while passing it the time elapsed from the previous call - for example, in animations.

    Title:

    #ifndef ANIMATION_ENGINE_H
    #define ANIMATION_ENGINE_H
    #include 
    namespace animation {
    // Actor function has one parameter: time delta since previous frame
    // It should return true if animation has finished,
    //      or false if it should go on
    typedef std::function actor_func;
    // Handler is called after the animation is complete
    typedef std::function handler_func;
    const handler_func doNothing = [] {};
    void start(unsigned intervalMs, actor_func, handler_func = doNothing);
    } // end of namespace animation
    #endif // ANIMATION_ENGINE_H
    


    The interface is simple: in start () we pass the interval of calls, a function and an optional completion handler.

    And implementation:

    #include "animation_engine.h"
    #include 
    namespace animation {
    void start(unsigned intervalMs, actor_func actor, handler_func handler)
    {
        std::thread t([=]() {
            auto timeStart = std::chrono::system_clock::now();
            float lastInterval = 0;
            while(1) {
                std::this_thread::sleep_for(std::chrono::milliseconds(intervalMs));
                float timeDelta = std::chrono::duration_cast
                        (std::chrono::system_clock::now() - timeStart).count();
                if (actor(timeDelta - lastInterval)) {
                    handler();
                    break;
                }
                lastInterval = timeDelta;
            }
        });
        t.detach();
    }
    } // end of namespace animation
    


    Here it’s already more interesting: on the basis of the functions passed by the arguments and the interval, a new function ( closure ) is built where the passed arguments are copied ([=] means capturing by value all the variables used in the lambda from the current scope). Then this new function is launched in a separate thread, where it chases our unfortunate actor function in an endless loop until it returns true. Well, or the application will not close. Next, start () ends, leaving the animation spinning in its stream.

    Below is an example of use. Suppose we are writing a toy like a ping-pong or arkanoid, and we need to actuate the ball. Here's how to implement it:
    animation::start (30, 
        [=] (float dt) {
            this->adjustBallDirection(dt); // bouncing, maybe gravitation
            this->updateBallPosition(dt);
            return (this->ballMissed()) // if player missed the ball -> stop
        },
        [this] {
             resetBall();
        }
    );
    


    Compile all this, as usual, it is necessary with the switch -std = c ++ 11 or -std = c ++ 0x. In addition, at the linking stage, you must pass the -pthread switch. If you, like me, work with QMake, just add these two lines to the .pro file:
    CONFIG += c++11
    QMAKE_LFLAGS += -pthread
    


    Possible improvement No. 1: for real-world tasks, it would be nice to improve the calculation of the interval between calls. For this, it is necessary to take into account the time until the actor function works out. And now it turns out that the interval is getting a little longer. But I did not bother this time with this: just not a note about it.

    Possible improvement No. 2: you can introduce additional functionality that would allow you to interrupt the animation. It is not difficult to do this (at least by introducing a flag that all streams with animations would check during operation), but, again, the example code would swell from this.

    It is likely that you know a more concise way to solve this problem - it will be cool if you state it in the comments. Or do you know a more interesting case where lambdas would simplify life ... in general, you know what to do.

    Thanks for attention!

    Also popular now: