This is a minimal reproducible example:
#include <memory>
#include <SFML/Graphics.hpp>
class Label
{
public:
Label(sf::String msg, float y)
{
if (!font.loadFromFile("arial.ttf"))
{
printf("Error loading font!\n");
}
text.setString(msg);
text.setPosition(0, y);
}
sf::Font font;
sf::Text text = sf::Text("Label", font, 40);
};
int main()
{
sf::RenderWindow window(sf::VideoMode(800, 600), "SFML window");
// label is a local variable
Label label1 = Label("Label1", 0);
// label is inside a pointer
std::unique_ptr<Label> label2 = std::make_unique<Label>("Label2", 50);
struct Labelstruct
{
Label label;
};
// label is inside a struct
struct Labelstruct label3 = { Label("Label3", 100) };
// label is inside a struct inside a pointer:
std::unique_ptr<Labelstruct> label4 = std::make_unique<Labelstruct>(Label("Label4", 150));
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
window.clear();
window.draw(label1.text);
window.draw(label2->text);
window.draw(label3.label.text);
window.draw(label4->label.text);
window.display();
}
return EXIT_SUCCESS;
}
Explanation: The class Label
wraps up a sf::Text
. Then I use four different ways to initialze and draw it:
"Label4" is not drawn. I can see a point where the label should appear:
Why does a pointer to a struct cause this problem?
For your convenience, this is the command I used to compile the thing:
g++ test.cc -std=c++20 -lsfml-window -lsfml-system -lsfml-graphics
from the sf::font docs.
It is important to note that the sf::Text instance doesn't copy the font that it uses, it only keeps a reference to it. Thus, a sf::Font must not be destructed while it is used by a sf::Text (i.e. never write a function that uses a local sf::Font instance for creating a text).
std::unique_ptr<Labelstruct> label4 = std::make_unique<Labelstruct>(Label("Label4", 150));
This line creates a Label
then uses it to move-construct another Label
that is inside Labelstruct
, the main constructor of Label
only runs once, and the Font
object it owns is destroyed, it is moved into the new object. but as the docs say the Text
object is keeping a reference to the Font
object that is destroyed.
your Label
object needs to store a std::shared_ptr
to the font, so the font reference is never invalidated.
class Label
{
public:
Label(sf::String msg, float y)
{
if (!font->loadFromFile("arial.ttf"))
{
printf("Error loading font!\n");
}
text.setString(msg);
text.setPosition(0, y);
}
std::shared_ptr<sf::Font> font = std::make_shared<sf::Font>();
sf::Text text = sf::Text("Label", *font, 40);
};
Note that sf::Font
objects are expensive. ideally, the Font should be created on the main
stack (or in a resource manager). you should have 1 Font object in your application and pass raw pointers to it to your labels, there is no reason for each Label
to own a separate font.