Weird error after updating to 1.6.1

Started by johnnywz00, 14 November 2024, 15:12:07

johnnywz00

Some time ago I wrote some SFML projects using TGUI 0.6, mainly to implement a high scores window. Now I've moved my work to a new computer and upgraded to 1.6.1. The first project seemed to go fine. The second project is almost copy-paste of the first in terms of the code that uses TGUI, to implement high scores.
I am getting a bad access error with the simple line
```
auto eb = tgui::EditBox::create();
```
What is most odd to me is if I cut that line and paste it as the very first line in `main()`, it still throws the error. And yet `tgui::Panel::create()` (and probably most others) do not throw the error. Any explanations?

The stack trace seemed to indicate it was on Backend::createText

Thanks!

texus

I'm assuming the message in the thrown error is about no backend existing?

You must first create a Gui object and attach a window to it before you are allowed to create any widgets. Otherwise the widgets can't create textures or texts.

johnnywz00

#2
The only message I see is "bad access". And this program worked until I moved the project to a new computer and upgraded to 1.6.1.
There is a Gui object.
There is no error trying to create other kinds of widgets, just EditBox.

ARM Silicon Mac running Sequoia

texus

I checked the code and it seems like there is no error in Release mode when attempting to use a backend that doesn't exist. If TGUI is built in debug mode then it would print something to the terminal and terminate.

The bad access happens on address 0x30, such a low address means that it tried to dereference a nullptr.
With that call stack, the only explanation that I see is that the backend would be a nullptr.

Try printing the value of "tgui::getBackend()" before creating the edit box. If my theory is right then it it will be a nullptr and calling "tgui::isBackendSet()" will return false. If this is the case then check where you are passing the window to the gui. This is the moment that the backend will be created, all widget creations should happen later. This restriction was added in TGUI 0.9 (when making the library work with other backends than SFML) so the old TGUI versions had no problems with creating widgets earlier.

Not all widgets create their resources immediately, which could explain why creating some widgets do work. I imagine that e.g. RadioButton will also cause the crash.

johnnywz00

I really appreciate the swift replies. You are correct that the backend is resulting in a nullptr, but I can't figure out why, because the TGUI-specific code seems to be the same in this problematic project as it is in a similar project that isn't throwing this error.

I never really read any docs about new protocol for using 1.6.1, but I stumbled across the fact that you now include a backend header and change tgui::GuiSFML to tgui::Gui. Are there any other things I need to change? Is it viable to have a Game class with `tgui::Gui  gui;`  and then call `gui.setTarget([RenderWindow&])` in the class constructor?

texus

QuoteAre there any other things I need to change?
None that I can think of.

QuoteIs it viable to have a Game class with `tgui::Gui  gui;`  and then call `gui.setTarget([RenderWindow&])` in the class constructor?
Yes. You just have to be careful about which objects you put in the same class.

TGUI is only fully constructed once you make the setTarget call. Suppose the class containing the Gui is called Game. If Game contains a member called MenuScreen, then the constructor of MenuScreen will execute before the constructor of Game. So if inside the MenuScreen constructor you call tgui::EditBox::create(), then you would have an issue.

I imagine that your issue is similar to the case described above.
Alternatively, maybe the class that contains the Gui is a global variable? That could also cause serious issues (although you probably would have already faced some before in that case).

Ideally you would create the Gui at the correct spot to avoid issues. If for some reason it is too difficult: there is a way to manually construct the TGUI backend. By default the Gui creates the backend internally to reduces the amount of work you have to do, but it has the downside of complicating the lifetime of the backend.
An alternative is thus to manually create the backend object. This can be done before the SFML window is even created, so you would essentially just put the following as the very first lines in your main function:
Code (cpp) Select
auto backend = std::make_shared<tgui::BackendSFML>();
backend->setFontBackend(std::make_shared<tgui::BackendFontFactoryImpl<tgui::BackendFontSFML>>());
backend->setRenderer(std::make_shared<tgui::BackendRendererSFML>());
tgui::setBackend(backend);
At the very end of your main function you would put the following line:
tgui::setBackend(nullptr);

When creating the backend manually like that, you can work with widgets before the Gui is constructed without any issues.

johnnywz00

The Game class has no members that construct TGUI elements. The Gui is not in a global. I have a growing suspicion that XCode could be messing something up. I renamed a directory or the project name or something that messed up tracking somehow. I will let you know if I find it out. In the meantime I may try to build the backend like you suggest and see if it's a workaround. But I'm probably not going to be content until I know why it's giving me this inexplicable behavior!

johnnywz00

Your instructions for explicitly building the backend before creating the EditBoxes have helped me at least get the program running. Thanks for that! I'd really rather not leave that block in my code, though, so I still want to get to the bottom of the issue. I'm trying to duplicate the project in a fresh XCode project, but for one reason and another I'm having trouble getting things running. This new project is claiming that it won't load the SFML libraries because they're not signed correctly. So many headaches for juniorish-level computer hack.

Separately: I don't remember having to run any explicit cleanup code for TGUI 0.6 when I first made these projects on an Intel Mac. Now I get BAD ACCESS errors from my class destructors that use TGUI. Does 1.6.1 require explicit cleanup steps that 0.6 didn't?

texus

#8
Quoteit won't load the SFML libraries because they're not signed correctly
I vaguely remember that you had to go into the macOS settings somewhere and press a button to add an exception for the unsigned library. And you had to do the whole thing once for each library (i.e. separately for sfml-system, sfml-window and sfml-graphics). But maybe there is some setting in xcode to completely disable this security.

QuoteNow I get BAD ACCESS errors from my class destructors that use TGUI. Does 1.6.1 require explicit cleanup steps that 0.6 didn't?
It doesn't require explicit cleanup, but no TGUI widgets or textures are allowed to exist anymore after the backend is destroyed (which happens when calling setBackend(nullptr) or in the destructor of the Gui object if you don't manually create a backend). If TGUI objects still exist afterwards then their destructors would crash at the end of the program.

Porting old code to fit these new restrictions can of course be annoying. If you write new code then it would immediately give you an error when doing something like that, and you know you have to change the code that you just wrote. When porting existing code it might be more difficult to find the issue and potentially redesign it a bit to make things execute nicely.

johnnywz00

The exit error is coming from Backend::unregisterFont, coming from destructor of an EditBox. But given that this is the same XCode project that was inexplicably throwing bad access to create EditBoxes (until explicitly building the backend), maybe there's a parallel reason that the destruction is wonky. I'm going to shelve this XCode project and try to get a fresh XCode project working with a copy of the same source code files. I really suspect XCode is playing some weird tricks here. Now if only creating a fresh project wasn't giving me headaches as well...
(By the way, I hope that I don't sound like I'm grouching at *you*, because I'm not, at all... TGUI's great! Computing in general is so fraught with gotchas for a guy that has limited free time...)

johnnywz00

I got the new XCode project to run. The EditBox error persists even with a fresh start. It must be something in my code structure, because as I said, I have a similar project on this computer that is running TGUI without error. The code lines that reference TGUI are identical between the two projects (it's the same  high score system), but a few of the lines are located slightly differently because the class structure is a little different. I'll examine that further.

johnnywz00

#11
Agh. My *other* project, (which didn't throw any errors creating the EditBoxes) *does* throw BAD ACCESS on exiting the program if the EditBoxes have been created. In this other project, there is a menu screen, where the EditBoxes aren't created till you enter the gameplay part. So if you open the program and quit without clicking the start game button, no error will occur. But in both programs, if the Gui creates EditBoxes, there is a bad access when the Game or GameState class destructs.
Could for some reason the m_registeredFonts vector be empty?

texus

#12
QuoteCould for some reason the m_registeredFonts vector be empty?
That wouldn't cause any issues. If it crashes in "Backend::unregisterFont" then it means that the backend is a nullptr and accessing m_registeredFonts is invalid.

If you print a line at the very end of your main function, does it get printed before it crashes?
I'm guessing the line gets printed and there is still some Font object that hasn't been destroyed yet.

Edit: depending on how much time you are willing to put in this, and what you really want out of the upgrade, maybe it would be better to only update to TGUI 0.8 for your existing project, which is similar enough to 0.6 while still supporting the latest SFML 2 version.

johnnywz00

#13
Yes, it prints. After main() finishes, ~Game kicks in which (in this case) calls ~StateManager which calls ~PlayingState, which appears to be calling the destructor of some vector related to an EditBox or a sharedptr of EditBox... ultimately reaching unregisterFont...
The "LetterInvadersState" which you see stores a bare pointer to the Gui object which is a member of the Game class. But LetterInvaders is the class that creates and uses the EditBoxes. Would there be a difference if I constructed all the Widgets in the Game class, and only displayed them through the LetterInvaders class?

texus

Is StateManager above or below the Gui in the declaration of Game?
Could you try placing the Gui object as first member in your Game object?

johnnywz00

Ah, the StateManager is indeed declared above the Gui. I will swap that now and see...

johnnywz00

Ah, that's it!! Thank you, and sorry that I'm so dense. I now have one TGUI project that's error-free and one project that still gets BAD ACCESS when I create EditBoxes unless I manually construct the backend. But I expect that that is a result of how my classes are structured and I am much more inspired to figure it out especially now that I know the restructuring fixed this problem.
Thanks Texus!

johnnywz00

Just letting you know that I have finally solved the EditBox error, and as suspected it was a result of my class structure. In this second project there is no StateManager and the GameState was getting some init called (in which the gui high scores are set up) in the Game constructor, *before* the gui's target had been set. Transposing the order of two lines fixed the problem... what an oversight! But I'm learning.
I've now got no remaining TGUI related errors and can keep happily programming away!