c++esp32

Pass variable with function pointer C++


I'm trying to pass a parameter along with a function pointer.

Currently I have this code that works, but I wanted to refactor it to avoid creating footprints(?) for the doUserAnimation calls.

#include <string>
#include <iostream>

typedef void (*AnimationFunction) ();

int gDefaultAnimationCount = 0;

struct LedAnimationArray 
{
    std::string name;
    AnimationFunction function;
};

void A_AllDark()
{
    std::cout << "BuiltInAllDark";
    std::cout << '\n';
}

void A_AllLight()
{
    std::cout << "BuiltInAllLight";
    std::cout << '\n';
}

void doUserAnimation(int animationNumber)
{
    std::cout << "UserAnimation_";
    std::cout << animationNumber;
    std::cout << '\n';
}

LedAnimationArray _builtInAnimations[]
{
    {"Off", A_AllDark},
    {"On", A_AllLight}
};


// [Start want to remove this code]

void UserAni1()
{
    doUserAnimation(0);
}

void UserAni2()
{
    doUserAnimation(1);
}

LedAnimationArray _userAnimations[]
{
    {"UserAni1",UserAni1},
    {"UserAni2",UserAni2}         
};

// [End want to remove this code]

LedAnimationArray getAnimation(int animationNumber)
{
    if (animationNumber > gDefaultAnimationCount - 1)
    {
        LedAnimationArray b = {"usertest",_userAnimations[animationNumber-gDefaultAnimationCount].function};
        
       // I want to try and do something like this, but that works :)
       //LedAnimationArray b = {"usertest",  doUserAnimation(animationNumber-gDefaultAnimationCount)};
    
       return b;
    }
    else
    {
        return _builtInAnimations[animationNumber];
    }
}

int main() 
{
    gDefaultAnimationCount = sizeof(_builtInAnimations) / sizeof(_builtInAnimations[0])  ; 
    
    getAnimation(0).function();
    getAnimation(1).function();
    getAnimation(2).function();
    getAnimation(3).function();
 
    return 0;
}

So I currently pass a pointer to a function within an array like this:

LedAnimationArray b = {"usertest",_userAnimations[animationNumber-gDefaultAnimationCount].function};

But this means I have to pre-define an array of all the possibilities.

I want to try and remove the _userAnimation array, and pass a function with a parameter instead.

Something like this:

LedAnimationArray b = {"usertest",doUserAnimation(animationNumber-gDefaultAnimationCount)};

Solution

  • Basically, something like this?

    Add

    #include <functional>
    

    and replace existing lines with

    typedef std::function<void()> AnimationFunction;
    
    LedAnimationArray b = {"usertest", [animationNumber]() { doUserAnimation(animationNumber-gDefaultAnimationCount); }};
    

    As for an explanation: std::function<Signature> can store pretty much any "callable" ("thing can be called") with the given signature (parameter and return types).

    The construct on the "b=" line is called a lambda function: it's basically a function without a name, and the ability to "capture" items from its environment. It is often used for small callbacks.
    In this particular case, std::bind or std::bind_front would have worked too, but I'm still kind of partial to lambdas. Especially as an introduction, because lambdas don't just interface a single function call: the bind-family is like a special case, while the lambdas are the generic case.

    In this example, the lambda "captures" your animationNumber and stores it alongside the function itself; this is managed by the compiler. Yes, you can tell it to "just capture everything I use", but this is my answer, and I like to be specific :-)
    You can also capture references to an item, instead of copies, but that wouldn't work here (and is generally pointless for scalar items anyway unless you want to modify them).

    The code inside is not executed here -- instead, the function code along with the captures is stored in that std::function thing, so it can be called at a later point.