2
\$\begingroup\$

After I've discussed pointer semantics with Loki Astari previously, I finally managed to code the TextureManager class using auto_pointers. But the problem is that I need to pass an SDL_Renderer object to my class' constructor as an auto_ptr. But the object I want to pass is a raw pointer.

How should I do it? Also, I'm not so sure about the aout_pointers I've written. Can you help me, please?

TextureManager.h

#pragma once //using visual C++

#include <unordered_map>
#include "Texture.h"
#include <memory>

/**
    This class helps the user to manage large 
    amount of texture at the same time (disposing them, setting 
    their color, their renderer, ...). The dispose method of this class 
    should be called before the end of the program (there will be no issues
    if it is not called : the destrctor of the TextureManager class will call
    the destructor of the @Texture class ! But it is better to call it, since
    it will release all ressources when you want !).This class contains a dynamic
    array, so the user can add @Texture pointers threw out the programs
    evolution.
*/

typedef std::unordered_map<int, Texture*> atlasType;//cannot use auto_ptr in STL container !

class TextureManager
{
public:
    TextureManager(std::auto_ptr<SDL_Renderer> pRenderer);
    ~TextureManager();

    void setRenderer(std::auto_ptr<SDL_Renderer> pRenderer);
    bool LoadFromFile(unsigned int ID, const char* fileName);

    bool unloadTexture(unsigned int ID);
    void dispose();

    Texture& getTexture(unsigned int ID);
    Texture getTextureCopy(unsigned int ID);

private:
    std::auto_ptr<SDL_Renderer> m_pRenderer;
    atlasType m_textureAtlas;
};

TextureManager.cpp

#include "TextureManager.h"


TextureManager::TextureManager(std::auto_ptr<SDL_Renderer> pRenderer)
    : m_pRenderer(pRenderer)
{}

TextureManager::~TextureManager(){
    dispose();
}

void TextureManager::dispose(){
    for (atlasType::iterator it = m_textureAtlas.begin(); it != m_textureAtlas.end(); ++it){
        delete (it->second);
    }
    m_textureAtlas.clear();
}


bool TextureManager::unloadTexture(unsigned int ID){
    Texture *pTempTexture = m_textureAtlas[ID];

    if (!pTempTexture)
        return false;

    delete pTempTexture;//dispose texture
    pTempTexture = nullptr;
    m_textureAtlas.erase(ID);

    return true;
}

Texture& TextureManager::getTexture(unsigned int ID){
    return *(m_textureAtlas[ID]);
}

Texture TextureManager::getTextureCopy(unsigned int ID){
    return *(m_textureAtlas[ID]);
}

bool TextureManager::LoadFromFile(unsigned int ID, const char* fileName){
    Texture *pTexture = m_textureAtlas[ID];

    if (pTexture == nullptr){
        pTexture = new Texture();
        pTexture->setRenderer(m_pRenderer.get());//Texture uses raw pointer;

        m_textureAtlas[ID] = pTexture;//will be deleted in dispose;
    }
    if (!pTexture->LoadFromFile(fileName))
        return false;

    return true;
}

void TextureManager::setRenderer(std::auto_ptr<SDL_Renderer> pRenderer){
    m_pRenderer = pRenderer;
}

Here's where I can not call the constructor (raw pointer <-> auto_ptr):

/* ......Declarations........... */

SDL_Renderer * m_pRenderer = nullptr;

/* etc..........*/

m_pRenderer = SDL_CreateRenderer(m_pWindow, -1, 0);


/*Trying to create TextureManager*/


m_textureManager = TextureManager(m_pRenderer);//fail to compile : is not of type auto_ptr<SDL_Renderer> ! what shall I do ?
\$\endgroup\$
1
\$\begingroup\$

To make you wrapper usabel as a rendorer:

class CWindowWrap
{
    public:
        operator SDL_Window*() {return window;}
        ....
};

// Now when you have a CWindowWrap you can use it anywhere an 
// SDL_Window* can be used. The compiler sees there is an 
// automatic conversion from CWindowWrap to a SDL_Window* and
// calls this function to do the conversion.
\$\endgroup\$
  • \$\begingroup\$ thanks, I wil add this to my code, and I think that it will be finished !!!! No more textureManaging !!!! \$\endgroup\$ – MattMatt Apr 25 '15 at 18:39
2
\$\begingroup\$

You don't need a texture copy.

Texture& getTexture(unsigned int ID);
// Texture getTextureCopy(unsigned int ID);

This works:

Texture&  t1 = tm.getTexture(1);  // Gets a reference.
Texture   t2 = tm.getTexture(2);  // Gets a reference but because t2
                                  // is an object copies it from the
                                  // original into `t2`

auto_ptr is deprecated

std::auto_ptr<SDL_Renderer>

Prefer the modern version std::unique_ptr it understands move semantics.

std::unique_ptr<SDL_Renderer, TypeThatKnowsHowToDeleteSDLRendor>  renderer;

Also std::unique_ptr and std::auto_ptr just call delete on the pointer. I am betting that the SDL library has a special function to delete render objects. In which case you need to add a class that knows what function to call.

Also std::unqiue_ptr can be used in containers:

typedef std::unordered_map<int, Texture*> atlasType;//cannot use auto_ptr in STL container !

But you don't even need to be using pointers. Texture is not polymorphic. Just create the object in the container (not the pointer).

std::unordered_map<int, Texture>  AtlasType;

Naming conventions.

It is usall to give User defined types an initial capitol letter. It is usual to give methods and member variables an initial lower case letter.

std::unordered_map<int, Texture>  AtlasType;
             //                   ^  Its a user defined type.
AtlasType                         textureAtlas;
             //                   ^ its an addressable object.

Prefer the new range based for

for (atlasType::iterator it = m_textureAtlas.begin(); it != m_textureAtlas.end(); ++it){
    delete (it->second);
}

As mentioned last time. Prefer auto, prefer std::begin/std::end over method calls. and prefer range based for over all of them:

//
for (auto it = std::begin(m_textureAtlas); it != std::end(m_textureAtlas); ++it){
    delete (it->second);
}

//
for(auto item: m_textureAtlas) {
    delete item.second;
}

//
std::for_each(std::begin(m_textureAtlas), std::end(m_textureAtlas),
               [](AtlasType::value_type& v){delete v.second;});

Compilation Failure:

 m_textureManager = TextureManager(m_pRenderer);
                               //  ^^^^^^^^^^^ that is not an auto_ptr

 // You have to create the auto_ptr object.
 std::auto_ptr<SDL_Renderer>   rend(m_pRenderer)
 m_textureManager = TextureManager(rend);

Note. When you give up ownership of the object you should not be using it from the original pointer again. The only way to access the render object should not be through the TextureManager that owns it. If you have kept another pointer to the renderer that you are cheating ownership semantics.

Writting a shard render object

class CRendWrap
{
    public:
        CRendWrap(SDL_Window* w, int x, int y)
            : SDL_CreateRenderer(w, x, y)
        {}
        ~CRendWrap()
        {
            if (rend != nullptr) {
                SDL_DestroyRenderer(rend);
            }
        }
        // Disable Copy
        CRendWrap(CRendWrap const&)             = delete;
        CRendWrap& operator=(CRendWrap const&)  = delete;
        // Allow Move
        CRendWrap(CRendWrap&& move)
            : rend(nullptr)
        {
            using std::swap;
            swap(rend, move.rend);
        }
        CRendWrap& operator=(CRendWrap&& move)
        {
            using std::swap;
            swap(rend, move.rend);
        }
    prviate:
        SDL_Renderer*   rend;
};

typedef std::shared_ptr<CRendWrap>   CRender;

// Usage.
CRender  renderer = std::make_shared<CRendWrap>(win, 10, 20);
\$\endgroup\$
  • \$\begingroup\$ Actually, there's no "type" which knows how to delete the Renderer ! So, as you said, I have to create a special class to destroy it! The SDL library provides a function called SDL_DestroyRenderer(SDL_Renderer renderer); \$\endgroup\$ – MattMatt Apr 24 '15 at 18:54
  • \$\begingroup\$ Thank by the way; I started C++ just a month ago; Now I think I won't make stupid semantic errors with pointers ! Also, 1) would it be a good idea to use a shared_ptr to represent the SDL_Renderer ? 2) If I do not use Texture pointers, will Texture instantly be deleted when I remove them from the map ? \$\endgroup\$ – MattMatt Apr 24 '15 at 18:58
  • \$\begingroup\$ 2) If I do not use Texture pointers, will Texture instantly be deleted when I remove them from the map ? 3) When you call 'auto', will the type be replaced at compile time ? 4) I agree with what you said at the end, but there's a little problem ! I need to make that auto_ptr decleration in a method called init() : when my program will reach the end of the scope, the ptr will be deleted and I'll have a null renderer in TextureManager! Thanks by the way, I started C++ a month ago, and you really helped me! \$\endgroup\$ – MattMatt Apr 24 '15 at 19:13
  • \$\begingroup\$ 1) If the renderer is used by many objects then yes a shared pointer is a good idea. BUT there is not way to a custom deleter. So you will need to write a quick wrapper class. So create a CRenderer class that holds an SDL_Renderer and correctly deletes it in the destructor. Then you can put the CRenderer in a shared_ptr. \$\endgroup\$ – Martin York Apr 24 '15 at 19:13
  • \$\begingroup\$ 2) Yes. When you erase the member from the container or call clear on the container or let the container be destroyed. Then the destructor of the Texture object is immediately called. \$\endgroup\$ – Martin York Apr 24 '15 at 19:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.