Search code examples
c++pointersfunction-pointerssfmlaccess-violation

Why do I receive an access violation error in my SFML C++ project when passing object and function pointers?


I'm currently learning c++ and I'm working on my first game using SFML/TGUI. I have tried making a function that creates a button, which in turn calls on a function when pressed. In an effort to make the button creator smarter and more versitile, I have made it so that it takes an object pointer and a function pointer as parameters. When the button then gets pressed, I want it to be able to call a member function from the object that was passed to it. It should also be noted that I got it working for a while, but then I changed something and now it doesn't work.

In my main.cpp, I create a gui and a pointer to this gui. I then pass the pointer into my Utility class, which consists of static variables and functions:

//Create gui object
tgui::GuiSFML gui{ window };

//Create pointer to gui object
tgui::GuiSFML* guiPointer = &gui;

//Pass guiPointer into Utility class
Utility::setup_getGuiPointer(guiPointer);

This is what is in my Utility class:

//Static gui pointer in Utility
static tgui::GuiSFML* guiPointer;

//Passes gui pointer from main into Utility
static void Utility::setup_getGuiPointer(tgui::GuiSFML* passedGuiPointer)
{
    guiPointer = passedGuiPointer;
}

The create button function in my utility class looks like this,

//Takes arguments: ObjectPtr, FunctionPtr, Size, Pos and Text to create button
template<typename Class>
inline tgui::Button::Ptr& Utility::u_createButton(Class* object, void(Class::* functionPointer)(tgui::GuiSFML* guiPointer, tgui::Button::Ptr& button), tgui::Layout2d size, tgui::Layout2d position, std::string buttonText)
{
        //Creates the button
    tgui::Button::Ptr button = tgui::Button::create(buttonText);

        //Sets size and position
    button->setSize(size);
    button->setPosition(position);

        //Adds button to gui
    guiPointer->add(button);

        //Sets onPress on the button to call the passed function from the passed objects,
        //passing the gui pointer and button pointer into this function.
    button->onPress([&]() { (object->*functionPointer)(guiPointer, button); });

        //Returns button pointer if it's needed before onPress.
    return button;
}

Now, I can then create a custom button from inside any class using the following line:

Utility::u_createButton(this, &ClassName::FunctionName, size, position, text;

And I create a button inside one of my classes, and the function that I pass a pointer to is just:

void ClassName::FunctionName(tgui::GuiSFML* guiPointer, tgui::Button::Ptr& button)
{
    std::cout << "Button Pressed" << std::endl;

        //The reson behind the passed arguments is so that the function itself can delete
        //the button after use if needed using the following line:
    //guiPointer->remove(button);
}

When I run this, it creates a button but gives the following error when I press it:

Unhandled exception at 0x85EEB600 in GUI_SFML_Template.exe: 0xC0000005: Access violation executing location 0x00000000.

The current stack frame was not found in a loaded module. Source cannot be shown for this location.

I don't understand why I get this error, though I believe it has something to do with my objects and functions trying to write onto the gui without the proper access to it. I would be very grateful if anybody could help me.


Solution

  • Your lambda is capturing by reference, all the local variables it captures references to will cause undefined behaviour when the button is pressed as the local variables will have gone out of scope. You should capture by value instead:

    button->onPress([object, functionPointer, guiPointer, button]() { (object->*functionPointer)(guiPointer, button); });
    

    Generally lambdas capturing all by reference are only safe to use within the function they're declared in and shouldn't be stored for long term use.