tgui::Gui initialize from class object?

Started by conleec, 02 September 2019, 22:38:26

conleec

I'm going thru an online SFML tutorial and building a very simple game. I thought as an exercise I would try to use TGUI in the project, just to expand my understanding.

Right off the bat I'm having an issue getting an instance of Gui.

In this tutorial, the small app has a Game class, a method of which creates an SFML RenderWindow dynamically like so:

Code (cpp) Select

void Game::initializeWindow() {
this->videomode.width = GAME_WIDTH;
this->videomode.height = GAME_HEIGHT;
this->window = new sf::RenderWindow(this->videomode, "My first game", sf::Style::Titlebar | sf::Style::Close);
}


Given that I have a pointer to this window object, I thought I might be able to create an accessor to it and feed it straight into the tgui::Gui initializer like so:

Code (cpp) Select

tgui::Gui gui(game.getWindow)


Or maybe even inside the Game class constructor, like this:

Code (cpp) Select

this->window = new sf::RenderWindow(this->videomode, "My first game", sf::Style::Titlebar | sf::Style::Close);
tgui::Gui gui(this->window);


How far off base am I to think that a pointer to a window object is similar to a reference to a window object, as in the online TGUI tutorial?

Code (cpp) Select

int main()
{
sf::RenderWindow window(sf::VideoMode(800, 600), "TGUI window");
window.setFramerateLimit(60);

tgui::Gui gui(&window);
...


How would one go about creating a tgui::Gui instance from a dynamically generated window within a class?

Thanks in advance for putting up with my newby-ness.

Chris

texus

You might want to look at some tutorial on references and pointers, as it is important to fully understand how memory is managed in order to avoid a lot of issues. Learning about std::shared_ptr is also going to be important when working with TGUI widgets, as Widget::Ptr is just a typedef for std::shared_ptr<Widget>.

Normally you have something like this. Both the window and gui are values stored on the stack. The tgui::Gui constructor takes a reference to the window as parameter, so although the code would be identical if it were to copy the window, nothing is copied when creating the gui.
Code (cpp) Select
sf::RenderWindow window(...);
tgui::Gui gui(window);


"&window" gives you a pointer to the window. This is not what the Gui constructor expects so you can't pass that. Although references are most likely implemented as pointers behind the scenes, references still act like objects in the code instead of as pointers. Pointers should actually be avoided when possible. The general rule is to use pointers when an object can be a nullptr (so for optional objects) and use references otherwise (or just copy them by value instead of referencing to the original object if it is something small like an int).

If your window is stored as a pointer then you need to dereference it. Notice the '*' in front of 'window'.
Code (cpp) Select
sf::RenderWindow &window;
tgui::Gui gui(*window);


So in your case, it would look like this:
Code (cpp) Select
tgui::Gui gui(*this->window);

I would actually advice against using a pointer for the window, the window is always going to be created with your Game, it is not something that can optionally exist. You will have to learn how to use the initializer list of the constructor (not to be confused with the completely unrelated std::initializer_list) in order to construct your window without using a pointer though.
Also, using "new" and "delete" is discouraged in modern c++. Smart pointers like std::unique_ptr and std::shared_ptr are the preferred way if you want to dynamically create objects.

Quote
Code (cpp) Select
void Game::initializeWindow()
In c++, using RAII is recommended over custom initialization functions. With your code, the Game object is in an invalid state between the moment it is constructed and the moment initializeWindow() is called. The principle of RAII is to put the code you have inside initializeWindow into the constructor of the Game object, so that it is in a valid state as soon as it is created and remains in a valid state for as long as the Game object exists (by not having a custom destroy function but putting cleanup code into the destructor).

It's a lot to process, but c++ isn't the most simple language to learn. And since TGUI quite heavily relies on modern c++, you might need to learn quite some things in order to properly use TGUI.

conleec

#2
Quote from: texus on 03 September 2019, 18:58:33
It's a lot to process, but c++ isn't the most simple language to learn. And since TGUI quite heavily relies on modern c++, you might need to learn quite some things in order to properly use TGUI.

Absolutely. As they say, Rome wasn't build in a day, right?  :)

And thank you texus for confirming what I've suspected while doing this YouTube tutorial...namely that some of the code is dubious. I appreciate your guidance. I'll probably complete the tutorial series, as it gets me some hands-on experience with some working examples, but will keep your comments in mind as I move on to future projects.

And yes, pointers and reference variables still have my mind in a flip-flop. On the surface they're simple, but they get complex very fast it seems.

Chris

texus

QuoteAnd yes, pointers and reference variables still have my mind in a flip-flop. On the surface they're simple, but they get complex very fast it seems.
Yeah, you can explain how to use them in a couple minutes, but the only way to really understand them is by actually using them in a few small programs.
Luckily you typically don't need the more complicated stuff like using pointers to an array of pointers or doing pointer arithmetic. Although pointer arithmetic could still be interesting to learn about if you want to understand how looping over a vector with iterators works (as std::vector<T>::iterator is just a T*).