How to get ptr properly?

Started by EpicMan2, 22 August 2017, 18:37:54

EpicMan2

Firstly i trying to initiate SFML and TGUI:
int main()
{
//Tons of code
Window window;
window.startUp();
window.createWindow();
}


void Window::createWindow()
{
sf::RenderWindow window{ { 800, 600 }, "Window" };
window.setFramerateLimit(60);
tgui::Gui gui{ window };

MainMenu menu;
menu.create(gui);

while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();

gui.handleEvent(event);
}

window.clear(sf::Color::White);
gui.draw();
window.display();
}
// Tons of code
}

Then i send a reference of TGUI context (gui) to another function where i trying to create main game menu:

void MainMenu::create(tgui::Gui & context)
{
context.removeAllWidgets();

GuiFactory guifactory;
Settings settings;

guifactory.button(context, 20, 4.5, 2.5, 10, "guisettings", "Settings");
tgui::Button::Ptr settingsbutton = context.get<tgui::Button>("Settings");
settingsbutton->connect("pressed", [&]() {
settings.create(context);
});
//Tons of unnecessary code
}


void Settings::create(tgui::Gui & context)
{
context.removeAllWidgets();

GuiFactory guifactory;

//Tons of unnecessary code
guifactory.checkbox(context, 20, 1.45, 28.0, 28.0, "settingsvsynccheckbox", "settingsvsynccheckbox", 0, 28);
tgui::CheckBox::Ptr settingsvsynccheckbox = context.get<tgui::CheckBox>("settingsvsynccheckbox");
if (getSettingBool("Fullscreen") == true) {
settingsvsynccheckbox->check();
}
else {
settingsvsynccheckbox->uncheck();
}
//Tons of unnecessary code

}

But when i push SettingsButton and application trying to render Settings Page i get a Nullptr error. Where my code gone wrong?
Btw, guifactory::checkbox and button methods:
Quotevoid GuiFactory::checkbox(tgui::Gui & context, double xpos, double ypos, double xsize, double ysize, std::string text, std::string indentifier, int type, int size)
{
   auto windowWidth = tgui::bindWidth(context);
   auto windowHeight = tgui::bindHeight(context);

   Localization localization;
   auto checkbox = std::make_shared<tgui::CheckBox>();
   checkbox->setSize(xsize, ysize);
   checkbox->setText(localization.wtext(text));
   checkbox->setTextSize(size);
   if (type == 1) {
      checkbox->setPosition(xpos, ypos);
   }
   else {
      checkbox->setPosition(windowWidth / xpos, windowHeight / ypos);
   }
   context.add(checkbox);
}
void GuiFactory::button(tgui::Gui& context, double xpos, double ypos, double xsize, double ysize, std::string text, std::string indentifier)
{
Localization localization;
auto windowWidth = tgui::bindWidth(context);
auto windowHeight = tgui::bindHeight(context);

auto Button = std::make_shared<tgui::Button>();
Button->setPosition(windowWidth / xpos, windowHeight / ypos);
Button->setSize(windowWidth / xsize, windowHeight / ysize);
Button->setText(localization.wtext(text));
Button->setTextSize(34);
context.add(Button, indentifier);
}

There is the same problem with comboboxes (i'm getting access violation error when trying to add any item), buttons work fine with it. If i add checkboxes and comboboxes as usual, then it works fine.

Compiler VS2015, Dynamically Linked, Prebuild Downloaded Libraries.

texus

#1
It is hard to say what goes wrong, but the following could be the issue:
Code (cpp) Select
settingsbutton->connect("pressed", [&]() {
    settings.create(context);
});


You are passing the settings to the lambda by reference (because of the "&"), but settings is a local object. After the "menu.create(gui);" call, the settings object has been destroyed. When the button is pressed, you will be accessing an object that no longer exists.

EpicMan2

Quote from: texus on 22 August 2017, 18:47:05
You are passing the settings to the lambda by reference (because of the "&"), but settings is a local object. After the "menu.create(gui);" call, the settings object has been destroyed. When the button is pressed, you will be accessing an object that no longer exists.

But why it doesn't crash when i use only buttons? (For test i have deleted all the checkboxes and comboboxes from Settings::create). For example, one of buttons:
guifactory.button(context, 20, 1.2, 2.5, 10, "guiexit", "Exit");
tgui::Button::Ptr exitbutton = context.get<tgui::Button>("Exit");
exitbutton->connect("pressed", [&]() {
mainmenu.create(context);
});


And can you give me advice how to properly pass TGUI context to other functions?

texus

I don't understand your code well enough to know what goes wrong exactly or how to solve it.
Accessing an object that is already deleted is undefined behavior, some things might work while others may not work. A crash is the most optimal scenario of things that can happen. Your code to create the buttons doesn't seem to access any members of the Settings class, only local variables and parameters, so no invalid memory is accessed. Maybe the code that creates the combo boxes does access something else. But a even if it doesn't, it is still wrong to access a reference to an object that no longer exists.

I don't know what you have in the Settings. If it doesn't need to store anything then its functions could be made static. Otherwise it could perhaps be a member variable of the MainMenu class instead of being a local variable in MainMenu::create, that way it will remain alive as long as the MainMenu exists. If it is a simple object that can be copied then the "&" in the lambda could also be replaced by a "=" to just copy it around.

Based on the way GuiFactory, Settings and Localization are created in those code snippets I would think static classes can be used there. But if the functions can be made static without any modification then of course there would be no reason for the crash you are experiencing.

Doesn't the debugger give some information about where the crash occurs? Getting a stacktrace when it crashes is extremely useful to figure out what goes wrong, otherwise I can only guess.