2

I'm working on this very basic texture handler class using the SFML library. and the code looks like this:

    // H
class TexturesHandler 
{
    friend class XML_Handler;
public:
    ...
    static TexturesHandler &init() { return m_handler; };
    sf::Texture &getTexture(std::string name);
    ...
private:

    ...
    static TexturesHandler m_handler;
    std::map<std::string, sf::Texture> m_Textures;
    void setTexture(std::string path, std::string name, const sf::IntRect& rect = sf::IntRect());
    ...
};
    // CPP
#include "TexturesHandler.h"
...
TexturesHandler TexturesHandler::m_handler;

sf::Texture &TexturesHandler::getTexture(std::string name)
{
    return m_Textures[name];
}

//private

...
void TexturesHandler::setTexture(std::string& path, std::string& name, const sf::IntRect& rect)
{
    sf::Texture texture;
    texture.loadFromFile(path, rect);
    m_Textures[name] = texture;
}

basically what this class does is load all textures at the beginning of the game

with the help of XML_Handler which is another class that reads the name, path and size of every texture from a XML file,and any sprite that needs a texture can call getTexture.

but the problem is that when the setTexture function is called for the first time a runtime exception is thrown at the 3rd line of the function from the header file xtree line 2046 with the message "read access violation _Wherenode was nullptr"

keep in mind that:

1- TextureHandler is a singlton class

2- by setting a break point inside the function setTexture it seems that all arguments passed by XML_Handler are correct and non of them is set to 0 or null...

3-I tries using emplace and insert functions to insert but it doesn't seem to work

I never got any similar exception before and searching for a solution doesn't seem to yield any results either. so my question is:

1-why do I get this exception when trying to insert a texture in the map ?

(optional)2- what are all the stl containers that use xtree as a base class ?

EDIT:

after a lot of testing it looks like the function setTexture is being called before the constructor . I tried to place a break point in both of them and the one in the function is getting hit before the one in the constructor. how is that even possible ???

also I still get the runtime exception when I try to access the map. even if it was something as simple as m_Textures["bla"];

another very bizarre thing is that even though the XML_Handler class is a friend it can access all data members and functions of the TextureHandler class except for the constructor so this is valid : TexturesHandler::init().~TexturesHandler();

while this : TexturesHandler::init().TexturesHandler(); gives the error:

type name is not allowed

Ronald joe
  • 339
  • 2
  • 9
  • 2
    Keep in mind that the `operator[]` of `std::map` creates a new texture, gives you a reference to it and then you assign it to `texture`. Are you sure that this is safe when the `Texture` inside the map is default-constructed? To find out more, I recommend splitting the third line into: `sf::Texture &insertAt = m_Textures[name]; insertAt = texture`. Then you will know whether the problem is during the construction or assignment. Also use `= std::move(texture)` to avoid copying. – Jan Schultke Aug 24 '20 at 18:28
  • Another possibility is maybe you are calling `setTexture()` on an invalid `TexturesHandler` object instance to begin with, such as via an invalid `TexturesHandler*` pointer or `TexturesHandler&` reference. That would cause an invalid `std::map` to be accessed. – Remy Lebeau Aug 24 '20 at 18:29
  • You can verify what @RemyLebeau said by checking whether `this` is a `nullptr` in a debugger. – Jan Schultke Aug 24 '20 at 18:31
  • as I said I set a few break points before to analyze and everything seems ok, the texture handler is called using `TextureHandler::init().setTexture(...)` so it is not a pointer. however I wanted to ask you if there is a way to insert a texture without calling the constructor – Ronald joe Aug 24 '20 at 18:34
  • @J.Schultke `this` is not guaranteed to be `nullptr` in the scenario I mentioned. It could literally be *any* random value. – Remy Lebeau Aug 24 '20 at 18:42
  • 2
    @Ronaldjoe "*the texture handler is called using `TextureHandler::init().setTexture(...)` so it is not a pointer*" - does `init()` return a reference instead? It is important to know. Please provide a [mcve] demonstrating the failure in action. – Remy Lebeau Aug 24 '20 at 18:43
  • @RemyLebeau please read the new edit – Ronald joe Aug 24 '20 at 18:50
  • 1
    *I tried to place a break point in both of them and the one in the function is getting hit before the one in the constructor.* -- Look at the call stack to see who is calling the function. – PaulMcKenzie Aug 24 '20 at 18:52
  • 1
    @Ronaldjoe your edit does not provide a [mcve]. What does `init()` actually look like, and where is it being called from exactly? – Remy Lebeau Aug 24 '20 at 18:53
  • by checking the call stack it seem once again that everything is functioning as it supposed to. `XML_handler` is calling the function `setTexture` and there is nothing wrong with it. how ever I have just provided another edit to show what exactly is the `init` function doing – Ronald joe Aug 24 '20 at 18:58
  • Unrelated: You can put the declaration of `m_handler` inside `init()` to make the singleton a little simpler: `static TexturesHandler& init() { static TexturesHandler m_handler; return m_handler; };` – Ted Lyngmo Aug 24 '20 at 19:00
  • @TedLyngmo I'll keep that in mind, thanks – Ronald joe Aug 24 '20 at 19:01
  • @Ronaldjoe `static TexturesHandler &init() { return m_handler; }` -- That is not the classic way to write a [Meyer's singleton](https://stackoverflow.com/questions/1661529/is-meyers-implementation-of-the-singleton-pattern-thread-safe). It is written as Ted Lyngmo pointed out. – PaulMcKenzie Aug 24 '20 at 19:02
  • Btw, is `getTexture()` supposed to return a default constructed `sf::Texture` if the texture name isn't found in the map? – Ted Lyngmo Aug 24 '20 at 19:03
  • @TedLyngmo this was supposed to be a small project just to test my skills so I'm not really interested in preventing such problems because I know that everytime the function will be called it will get a valid name. – Ronald joe Aug 24 '20 at 19:06
  • Ok, still, while searching for an elusive bug, I'd make it `return m_Textures.at(name);` – Ted Lyngmo Aug 24 '20 at 19:08
  • @Ronaldjoe *because I know that everytime the function will be called it will get a valid name* -- How do you know this? You shouldn't write programs believing things are going to work the way you think they will work. You really should be checking with `at()` or some other means whether that name doesn't exist in the map. The non-existing map entry and using `[ ]` is one of the most common bugs that exists when using `std::map`. – PaulMcKenzie Aug 24 '20 at 19:10
  • guys I really do appreciate your concern but that is not the problem right now. I'm one hair away from throwing my computer out of the window. I mean I would really appreciate it if you can help me in the actual problem – Ronald joe Aug 24 '20 at 19:12
  • 1
    You didn't give us a [mcve]. What else can we tell you other than look at the code you posted and take guesses? I'm sure if we had the entire program, within a short time, maybe minutes, maybe even seconds, we could tell you what the problem is. – PaulMcKenzie Aug 24 '20 at 19:14
  • Since `m_handler` looks like a global variable, if a variable in another source file access it you can get this problem. Are there other global variables or constructors that access it (possibly thru the buggy `init`)? – 1201ProgramAlarm Aug 24 '20 at 19:22
  • @Ronaldjoe -- Also, did you actually read about the Meyer's singleton, and how it should be implemented? Your implementation differs in various areas that may be critical to the issue you're seeing now. In your implementation, do you know when `m_handler` will be created? I'm not even sure, and probably it is unspecified. In the Meyer's singleton, creation of `m_handler` will be done *within* the `init()` function, so it is obvious when the actual `m_handler` will be created. – PaulMcKenzie Aug 24 '20 at 19:24
  • 1
    The general pattern is to "create on first use". Instead, you're at the mercy of the runtime to create that variable, and you're hoping that when you access it for the `return`, it has been created fully. – PaulMcKenzie Aug 24 '20 at 19:31
  • @PaulMcKenzie but it is still a static variable so it is created as soon as the header file is included as far as I know ?also please read the edit – Ronald joe Aug 24 '20 at 19:34
  • 2
    No, that is not how it works if you have essentially `static` variables acting as globals. [Read this](https://stackoverflow.com/questions/29822181/prevent-static-initialization-order-fiasco-c). Why don't you simply change the function into a proper Meyer's singleton, and retest? – PaulMcKenzie Aug 24 '20 at 19:35
  • `static TexturesHandler m_handler;` -- That declaration needs to be defined in one of your source files. Do you know when that source file gets a hold of creating that variable so that it can be used? I don't know, you don't know, no one knows, even if we had the all the code in front of us. This looks like a static order initialization issue. – PaulMcKenzie Aug 24 '20 at 19:49

0 Answers0