can't find where virtual method is being called from
Oct 2, 2020 at 2:15am UTC
hello
i'm creating a simple gui system for games (as it's just for learning purposes, i'd like to create it from scratch).
but I can't find where this pure virtual function is being called from. I know i'ts about 100 lines of code, but this is the minimal example that I could get that reproduces the problem:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
#include <vector>
#include <string>
#include <functional>
///Widgets
class Button{
protected :
std::function<void ()> action;
public :
virtual void run() = 0;
};
class TextButton : public Button{
public :
TextButton(const std::function<void ()>&);
void run();
};
TextButton::TextButton(const std::function<void ()>& function){
action = function;
}
void TextButton::run(){
action();
}
///Gui Manager
class GuiManager{
public :
std::vector<TextButton> m_text_buttons;
void addTextButton(const std::function<void ()>&);
void removeAll();
void run();
};
void GuiManager::addTextButton(const std::function<void ()>& function){
m_text_buttons.push_back(TextButton(function));
}
void GuiManager::removeAll(){
m_text_buttons.clear();
}
void GuiManager::run(){
for (auto &n : m_text_buttons) n.run();
}
///Menu Class that uses GUI
enum MenuHandlerEnum{Title, NewGame, LoadGame, Options, Credits, Exit};
class MenuHandler{
public :
MenuHandlerEnum m_current_menu;
GuiManager m_gui;
void changeMenu(MenuHandlerEnum);
void startMainMenu();
void startNewGameMenu();
MenuHandler();
void processEvents();
};
MenuHandler::MenuHandler(){
changeMenu(MenuHandlerEnum::Title);
}
void MenuHandler::processEvents(){
m_gui.run();
}
void MenuHandler::startMainMenu(){
m_gui.addTextButton([&](){changeMenu(MenuHandlerEnum::NewGame);});
m_gui.addTextButton([&](){changeMenu(MenuHandlerEnum::LoadGame);});
m_gui.addTextButton([&](){changeMenu(MenuHandlerEnum::Options);});
m_gui.addTextButton([&](){changeMenu(MenuHandlerEnum::Credits);});
m_gui.addTextButton([&](){changeMenu(MenuHandlerEnum::Exit);});
}
void MenuHandler::startNewGameMenu(){
m_gui.addTextButton([&](){changeMenu(MenuHandlerEnum::Title);});
m_gui.addTextButton([&](){changeMenu(MenuHandlerEnum::NewGame);});
}
void MenuHandler::changeMenu(MenuHandlerEnum new_menu){
m_gui.removeAll();
switch (new_menu){
case MenuHandlerEnum::Title:{
startMainMenu();
break ;
}
case MenuHandlerEnum::NewGame:{
startNewGameMenu();
break ;
}
default :{
m_current_menu = MenuHandlerEnum::Title;
break ;
}
}
m_current_menu = new_menu;
}
///Main
int main(){
MenuHandler menu;
menu.processEvents();
return 0;
}
if I execute it, i gte the error
pure virtual method called
terminate called without an active exception
Aborted
is it calling the method from inside the base class (Button)? if yes, why, and when exactly?
thanks in advance!
Oct 2, 2020 at 4:13am UTC
The problem is that GuiManager::run() is calling run() on each of the TextButtons, which is equivalent to activating the buttons. The actions for all the buttons involve calling MenuHandler::changeMenu(), which calls GuiManager::removeAll(), which clears GuiManager::m_text_buttons.
In other words, you're modifying an std::vector as you traverse it.
This would fix the crash, although I doubt it's what you intended:
1 2 3
auto v = std::move(m_text_buttons);
for (auto &n : v)
n.run();
You'll probably want a display() function or something.
Oct 2, 2020 at 5:04am UTC
this is the minimal example that I could get that reproduces the problem
This program contains multiple layers of trivial passthrough functions and purposeless class types.
There is some abstract class hierarchy being managed by a manager which is handled by some handler but the program just calls some functions in a list.
If the program was written to solve the problem directly, it would be more obvious that it modifies the list of functions while iterating over it.
This snippet reproduces the failure:
1 2 3 4 5 6 7 8 9 10
#include <vector>
#include <functional>
int main()
{
std::vector<std::function<void ()>> manager
{ [&]{ manager.push_back([]{}); manager.push_back([]{}); }, []{} };
for (auto &component: manager) component();
}
Last edited on Oct 2, 2020 at 5:07am UTC
Oct 2, 2020 at 12:34pm UTC
In other words, you're modifying an std::vector as you traverse it.
I actually hadn't realized that. so after modifying it, i tried to call the action() from a place in memory where there were nothing?
This program contains multiple layers of trivial passthrough functions and purposeless class types.
yes. as I said, its the minimal I could get still reproducing the error, so its not built from scratch, but a code stripped down.
anyway, thanks everybody! i changed the run() function in TextButton (line 22) to:
1 2 3 4 5 6 7
bool TextButton::run(){
if (buttonPressed()){
action();
return true ;
}
else return false ;
}
and the GuiManager run() (line 43) to:
1 2 3
void GuiManager::run(){
for (auto &n : m_text_buttons) if (n.run()) break ;
}
now if any button is pressed (the code for checking it isn't in the example, of course), it breaks the 'for' loop.
Last edited on Oct 2, 2020 at 12:37pm UTC
Oct 2, 2020 at 4:51pm UTC
i tried to call the action() from a place in memory where there were nothing?
You tried to use an object that had already been destructed.
Topic archived. No new replies allowed.