Autor Wątek: Bałagan w kodzie, pomoc przy posprzątaniu i zaprojektowaniu  (Przeczytany 1457 razy)

Offline LizarD

  • Użytkownik

# Marzec 11, 2012, 17:13:19
Witam!

Mam taką prośbę, ponieważ nie wiem czy sobie bałaganu nie narobię/nie narobiłem tym co zaprojektowałem. Chodzi o resource manager, wymyśliłem sobię taką koncepcję, są dwie klasy główne CResourceCreator, i CResourceManager pierwsza z nich odpowiada za zasoby które tworzy się w kodzie a druga za zasoby wczytywane z pliku
template <typename T>
class CResource
{
public:
         T get()              { return cResource; }

          bool isLoaded()
          {
                 if(cResource)
                       return true;
           
           return false;
           }
protected:
             T cResource;
};
Jeszcze taka mała dygresja, czy zmienna cResource nie powinna być typu std::unique_ptr ?
Tak się zastanawiam.... ten wskaźnik pozwala przechowywać zasób tylko jednemu obiektowi, więc co jak będę zwracał wskaźnik funkcją get ? Ten zostanie wyzerowany... ?

To jest klasa Resource Managera która odpowiada za zasoby które trzeba utworzyć a nie wczytać z pliku np VertexBuffer IndexBuffer....
template <typename>
class CResourceCreator
{
public:
         // Zwalnia zasób/wszystkie zasoby ale nie usuwa ich z wektora
         void release(T res);
         void releaseAll();
         
         // Usuwa zasób/wszystkie zasoby z wektora ale nie zwalnia ich !! ( nie wiem jeszcze po co ta metoda )
         void free(T res);
         void freeAll();

        // Jako że std::shared_ptr ma licznyk referencji, i zasób nie zostanie zwolniony dopuki ten licznyk jest większy od 0, a taki przypadek będzie zawsze ponieważ jedna referencja jest trzymana w managerze, ta metoda ma wyszukać zasoby z licznikiem referencji równym 1 i je zwolnić
        void refresh();

bool exist(std::shared_ptr<T> ptr);

protected:
                 std::vector<std::shared_ptr<T>> cResourceVector;
};
Analogicznie z zasobami które trzeba wczytać z pliku.

Użycie wygląda tak:
class CTexture2D    : public CResource<IDirect3DTexture9*>
{
public:

protected:
    UINT cWidth;
    UINT cHeight;

    std::string path;
};

class CTexture2DManager       : public CResourceCreator<CTexture2D>
{
public:
          std::shared_ptr<CTexture2D> load(const std::string& path);
          bool reload(std::shared_ptr<CTexture2D> ptr);
          bool reload(std::shared_ptr<CTexture2D> ptr, const std::string& path);
protected:

};

Wygląda to normalnie i elegancko, no ale klasa CResourceCreator jest szablonem, są w niej metody np release która ma zwolnić zasób tzn wywołuję metodę na obiekcie klasy, nie wiem jak to dobrze wytłumaczyć, ale ta klasa nie wiem czy zostanie wybrany szablon który będzie miał ten obiekt:
template <typename T>
class B{
public:
    void funkcja()
    {
          klasa.g; // I tutaj może zajść sytuacja kiedy szablon będzie taki że nie będzie w nim zmiennej g
     }

     T klasa;
};
Nie stanowi to jakiegoś dużego problemu bo ja tymi klasami zarządzam i zawsze w nich będą odpowiednie zmienne, no ale czy to jest eleganckie rozwiązanie, czy tak powinno być ?

Offline Mr. Spam

  • Miłośnik przetworów mięsnych

Offline Anton Chigurh

  • Użytkownik

# Marzec 12, 2012, 00:27:29
Niestety nie jestem profesjonalnym programistą, więc odpowiem "na oko", może niezbyt składnie i wyczerpująco ale za to w dobrej wierze ;-).

Klasa "CResource" wygląda na zbędną, bo jest praktycznie równoważna wskaźnikowym wrapperom typu "shared_ptr".
Klasa "CResourceCreator" jest prawie równoważna tablicy wskaźnikowych wrapperów.
Innymi słowy, jest tu za dużo owijania klas w inne klasy, przez co powstają niepotrzebne warstwy abstrakcji w kodzie.

Notka o unique_ptr - żeby go gdziekolwiek przekazać potrzebne jest użycie std::move. Move unieważnia oryginał i umożliwia wykorzystanie "przeniesionego" obiektu gdzieś indziej. Generalnie chyba nie to miałeś na myśli a raczej "shared_ptr".

Ja nie używałbym mądrych wskaźników do zarządzania zasobami (przynajmniej w prostej grze), bo prościej i wydajniej jest zaalokować pulę obiektów wewnątrz managera i zwracać na zewnątrz zwykłe wskaźniki. Ownership zasobów jest zawsze po stronie managera (stąd jego nazwa) więc smart pointery nie mają chyba sensu (chyba że jako zabezpieczenie że destruktor tablicy wewnątrz managera wywoła też destruktory obiektów zasobów), zwłaszcza jak pojedziesz po bandzie i spróbujesz alokować zasoby w ciągłym kąsku pamięci.

I zastanowiłbym się, czy na pewno chcesz mieć te zasoby polimorficznie. W momencie w którym dojdziesz do pokusy używania dynamic_cast, pewnie stwierdzisz, że jednak nie chcesz ;-).

Offline LizarD

  • Użytkownik

# Marzec 12, 2012, 13:30:51
Klasę CResource dodałem po to aby "zadbać" o taką samą nazwę zmiennej każdego zasobu.
Co do shared_ptr, np tekstury czy modele żeby ich nie powielać to trzeba się odwoływać do już wczytanych, i może zajść sytuacja kiedy usunie się zasób a jakiś inny obiekt klasy będzie korzystał z tego zasobu, więc tutaj chyba lepiej mieć licznik referencji.

Puki co to szukam jakiegoś pomysłu aby to raz, dobrze i wygonie napisać.

Offline LizarD

  • Użytkownik

# Marzec 16, 2012, 09:57:11
Przyszło mi jeszcze inne rozwiązanie które polega na zmodyfikowaniu tego co pokalałem:
class CResTemplate
{
public:
         virtual std::shared_pt<T> load();

         bool reload();
         void release();
        ....
private:
         static std::map<string,  shared_prt<T>> cResourceMap;
};


class CTexture  : public CResTemplatr<CTexture>
{
public:
         std::shared_ptr<CTexture> load(...);
};
Ciężko mi się na coś zdecydować. To rozwiązanie eliminuje ten problem ze wskaźnikiem....