• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Segmentation fault - std::unordered_map<std::string, T>

Object Storage Arubacloud
0 głosów
1,065 wizyt
pytanie zadane 6 kwietnia 2018 w C i C++ przez Mateusz Tocha Bywalec (2,560 p.)

Cześć widziałem parę podobnych, tematów, lecz wydaje mi się że u mnie jest problem złej organizacji projektu.

Problem polega wyrzucenie wyjątka "Segmentation fault", w momencie usuwania textury,

 

Żeby nie zaciemniać:

 

int main(void)
{
    DisplayMenager &dispMngr = DisplayMenager::getInstance();
    dispMngr.startup({800, 600});
 (...)
    glfwTerminate();
    return 0;
}

Display menager jest singletonem (coś mi się wydaje że tutaj już pierwszy błąd popełniłem), robi startup operacji.

void DisplayMenager::startup(glm::vec2 wndSize)
{
    this->width = wndSize.x;
    this->height = wndSize.y;
    this->init();
    this->enableDebug();
    this->prepare();
}


void DisplayMenager::prepare()
{
    //SETUP THE BLENDING MODE r-(1-a) g-(1-a) b-(1-a)
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_BLEND);
    this->LoadResource();
}


void DisplayMenager::LoadResource()
{       
    std::shared_ptr<Shader> tempShader(new Shader("res/shaders/Basic.vert"));
    this->_rscMngr.Add<Shader>("BasicShader", tempShader);    
    std::shared_ptr<Texture> tempTexture(new Texture("res/point.png"));
    this->_rscMngr.Add<Texture>("point", tempTexture);  
}

Pole  this->_rscMngr wyglada tak:

class DisplayMenager
{
  private:

    DisplayMenager();
    DisplayMenager(DisplayMenager const &);
    void operator=(DisplayMenager const &);

     ResourceMenager &_rscMngr=ResourceMenager::getInstance();

    (...)


  public:
    static DisplayMenager &getInstance()
    {
        static DisplayMenager instance;
        return instance;
    }
};

 

Sam ResourceMengaer również jest singletonem - zależało mi żeby mieć dostęp do wszystkich zasobów z każdego miejsca programu - tak wiem że nie jest to thread safe, ale na razie ni przychodzi mi nic innego do głowy, no może factory pattern ...

 

class ResourceMenager
{

private:
  std::unordered_map<std::string, std::shared_ptr<Shader>> Shaders;
  std::unordered_map<std::string, std::shared_ptr<Texture>> Textures;

  ResourceMenager() {}
  ResourceMenager(ResourceMenager const &) {}
  void operator=(ResourceMenager const &) {}

public:
  static ResourceMenager &getInstance()
  {
    static ResourceMenager instance;
    return instance;
  }
  ~ResourceMenager(){ 
  }
  template <typename T>
  void Add(const std::string &name, T &resource);

  template <typename T>
  void Add(const std::string &name, std::shared_ptr<T> resource);

  template <typename T>
  T getResource(const std::string &name);
};

 

oraz cześć cpp

template <>
void ResourceMenager::Add<Texture>(const std::string &name, std::shared_ptr<Texture> resource){
    this->Textures.emplace(name, resource);
}

 

Segmentation fault wchodzi przy wychodzeniu z programu, przy wywoływaniu (niejawnie) metody clear na unordered_map, 

i prowadzi do 

Texture::~Texture()
{
    glDeleteTextures(1, &(this->_RendererID));
}

Wygląda mi to na jakiś problem z wywłaszczaniem, bo gdy ResourceMenager jest w main() nic zlego sie nie dzieje.

Wszystko generowane w cmake , a kompilowane :

gcc (Rev1, Built by MSYS2 project) 7.3.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Flagi:

add_definitions(-DGLEW_STATIC)
add_definitions(-D_WIN32=1)
add_definitions(-Dfpermissive)
add_definitions(-Dj8)

Pozdrawiam.

 

 

 

komentarz 7 kwietnia 2018 przez Eryk Andrzejewski Mędrzec (164,260 p.)
Powinno być Manager, nie Menager.
1
komentarz 7 kwietnia 2018 przez Mateusz Tocha Bywalec (2,560 p.)
Czy to coś zmieni, albo wniesie merytorycznie do samego problemu? Dzięki poprawie.
komentarz 7 kwietnia 2018 przez Eryk Andrzejewski Mędrzec (164,260 p.)

Do tego przecież służy komentarz, aby zwrócić uwagę na rzeczy niekoniecznie związane z problemem. Gdyby to było rozwiązanie problemu, to umieściłbym je w odpowiedzi.

A chyba warto wiedzieć, jak się poprawnie coś pisze, żeby nie robić błędów. smiley

komentarz 7 kwietnia 2018 przez Mateusz Tocha Bywalec (2,560 p.)
Jasne dlatego podziękowałem.

3 odpowiedzi

+1 głos
odpowiedź 7 kwietnia 2018 przez j23 Mędrzec (194,920 p.)

ResourceMenager jest w main() nic zlego sie nie dzieje.

Być może ma to związek z kolejnością niszczenia singletonów - jeśli DisplayMenager jest niszczony przed ResourceMenager, wtedy jest błąd. Spróbuj tak: w destruktorze klasy DisplayMenager wymuś zwolnienie zasobów trzymanych przez ResourceMenagera.

 

Z drugiej strony, dlaczego DisplayMenager nie jest właścicielem ResourceMenagera?

 

add_definitions(-Dj8)

Oj, to chyba jest źle. Opcję -j8 powinieneś dać przy wywołaniu make'a.

komentarz 7 kwietnia 2018 przez Mateusz Tocha Bywalec (2,560 p.)

Dzięki za odpowiedź.

 

Z drugiej strony, dlaczego DisplayMenager nie jest właścicielem ResourceMenagera?

Otóż jest: 

class DisplayMenager
{
  private:
(...)
    ResourceMenager &_rscMngr=ResourceMenager::getInstance();
(...)
};

 

Być może ma to związek z kolejnością niszczenia singletonów - jeśli DisplayMenager jest niszczony przed ResourceMenager, wtedy jest błąd. Spróbuj tak: w destruktorze klasy DisplayMenager wymuś zwolnienie zasobów trzymanych przez ResourceMenagera.

DisplayMenager::~DisplayMenager()
{
    this->_rscMngr.~ResourceMenager();
}

dalej wyrzuca segmentation fault.

 

Call stack mówi:

glDeleteTextures (Unknown Source:0)
Texture::~Texture(Texture * const this) (c:\Users\toch\Desktop\OpenGL\exercises\src\Texture.cpp:31)
std::_Sp_counted_ptr<Texture*, (__gnu_cxx::_Lock_policy)2>::_M_dispose(std::_Sp_counted_ptr<Texture*, (__gnu_cxx::_Lock_policy)2> * const this) (c:\msys64\mingw64\include\c++\7.3.0\bits\shared_ptr_base.h:376)
std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release(std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2> * const this) (c:\msys64\mingw64\include\c++\7.3.0\bits\shared_ptr_base.h:154)
std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count(std::__shared_count<(__gnu_cxx::_Lock_policy)2> * const this) (c:\msys64\mingw64\include\c++\7.3.0\bits\shared_ptr_base.h:684)
std::__shared_ptr<Texture, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr(std::__shared_ptr<Texture, (__gnu_cxx::_Lock_policy)2> * const this) (c:\msys64\mingw64\include\c++\7.3.0\bits\shared_ptr_base.h:1123)
std::shared_ptr<Texture>::~shared_ptr(std::shared_ptr<Texture> * const this) (c:\msys64\mingw64\include\c++\7.3.0\bits\shared_ptr.h:93)
std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::shared_ptr<Texture> >::~pair(std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::shared_ptr<Texture> > * const this) (c:\msys64\mingw64\include\c++\7.3.0\bits\stl_pair.h:198)
__gnu_cxx::new_allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::shared_ptr<Texture> > >::destroy<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::shared_ptr<Texture> > >(__gnu_cxx::new_allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::shared_ptr<Texture> > > * const this, std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::shared_ptr<Texture> > * __p) (c:\msys64\mingw64\include\c++\7.3.0\ext\new_allocator.h:140)
std::allocator_traits<std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::shared_ptr<Texture> > > >::destroy<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::shared_ptr<Texture> > >(std::allocator_traits<std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::shared_ptr<Texture> > > >::allocator_type & __a, std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::shared_ptr<Texture> > * __p) (c:\msys64\mingw64\include\c++\7.3.0\bits\alloc_traits.h:487)
std::__detail::_Hashtable_alloc<std::allocator<std::__detail::_Hash_node<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::shared_ptr<Texture> >, true> > >::_M_deallocate_node(std::__detail::_Hashtable_alloc<std::allocator<std::__detail::_Hash_node<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::shared_ptr<Texture> >, true> > > * const this, std::__detail::_Hashtable_alloc<std::allocator<std::__detail::_Hash_node<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::shared_ptr<Texture> >, true> > >::__node_type * __n) (c:\msys64\mingw64\include\c++\7.3.0\bits\hashtable_policy.h:2084)
std::__detail::_Hashtable_alloc<std::allocator<std::__detail::_Hash_node<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::shared_ptr<Texture> >, true> > >::_M_deallocate_nodes(std::__detail::_Hashtable_alloc<std::allocator<std::__detail::_Hash_node<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::shared_ptr<Texture> >, true> > > * const this, std::__detail::_Hashtable_alloc<std::allocator<std::__detail::_Hash_node<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::shared_ptr<Texture> >, true> > >::__node_type * __n) (c:\msys64\mingw64\include\c++\7.3.0\bits\hashtable_policy.h:2097)
std::_Hashtable<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::shared_ptr<Texture> >, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::shared_ptr<Texture> > >, std::__detail::_Select1st, std::equal_to<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::hash<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true> >::clear(std::_Hashtable<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::shared_ptr<Texture> >, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::shared_ptr<Texture> > >, std::__detail::_Select1st, std::equal_to<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::hash<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true> > * const this) (c:\msys64\mingw64\include\c++\7.3.0\bits\hashtable.h:2029)
std::unordered_map<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::shared_ptr<Texture>, std::hash<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::equal_to<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::shared_ptr<Texture> > > >::clear(std::unordered_map<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::shared_ptr<Texture>, std::hash<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::equal_to<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::shared_ptr<Texture> > > > * const this) (c:\msys64\mingw64\include\c++\7.3.0\bits\unordered_map.h:845)
ResourceMenager::~ResourceMenager(ResourceMenager * const this) (c:\Users\toch\Desktop\OpenGL\exercises\src\ResourceMenager.h:26)
DisplayMenager::~DisplayMenager(DisplayMenager * const this) (c:\Users\toch\Desktop\OpenGL\exercises\src\DisplayMenager.cpp:9)
__tcf_1() (c:\Users\toch\Desktop\OpenGL\exercises\src\DisplayMenager.h:36)
msvcrt!_initterm_e (Unknown Source:0)
msvcrt!_wfindnexti64 (Unknown Source:0)
ntdll!RtlDeactivateActivationContextUnsafeFast (Unknown Source:0)

Oj, to chyba jest źle. Opcję -j8 powinieneś dać przy wywołaniu make'a.

Racja, wyrzuciłem to i dałem w  setting.json (Visual Studio Code)

    //CMAKE CONFIG

    "cmake.generator": "MinGW Makefiles",
    "cmake.mingwSearchDirs": [
       "C:\\msys64\\mingw64"
      ],
    "cmake.cmakePath": "C:\\Program Files\\CMake\\bin\\cmake.exe",   
    "glsllint.glslangValidatorPath": "C:/msys64/glslangValidator.exe",
    "cmake.parallelJobs": 14,
    "codegnuglobal.executable": "C:\\msys64\\usr\\bin\\global.exe",
    "[cmake]": {
    }

Czuje że robię złe podejście do tego, może IOC z Dependency Injection będzie się lepiej sprawowoać niż cisnąć  z tymi Singletonami.

komentarz 7 kwietnia 2018 przez j23 Mędrzec (194,920 p.)

Otóż jest: 

Ale, jak rozumiem, jest to statyczna zmienna a nie pole klasy.

 

dalej wyrzuca segmentation fault.

Nie możesz tak zrobić. Jawne wywoływanie destruktora jest (z jednym wyjątkiem) błędem. Teraz dwa razy wywoływany jest destruktor klasy ResourceMenager.

0 głosów
odpowiedź 7 kwietnia 2018 przez Mateusz Tocha Bywalec (2,560 p.)
edycja 7 kwietnia 2018 przez Mateusz Tocha

Hmm sam obiekt istnieje w momencie w, ktorym chce go usunąć, ale problem tutaj chyba jest z wywłaszczaniem dostępu do pamięci.

 

Tak sobie myśle czy funkcja glDeleteTexture(1, &(this->RenderID)) usuwa dane z pamięci karty graficzne, a związku z tym czy nie Context powinien jeszcze istnieć czy to nie ma znaczenia.

 

0 głosów
odpowiedź 7 kwietnia 2018 przez Mateusz Tocha Bywalec (2,560 p.)
edycja 7 kwietnia 2018 przez Mateusz Tocha

Właśnie zacząłem czytać:

Common Mistakes , pierwsze akapity już mówią że class Texture jest do poprawy.

 

#pragma once
#include <iostream>
#include <GL/glew.h>
#include <GL/GL.h>
#include <memory>

class Texture
{
private:
  std::string _FilePath;
  unsigned char *_LocalBuffer;
  int _width, _height, _BPP;

  void Release()
  {
    glDeleteTextures(1, &_RendererID);
    _RendererID = 0;
  }

public:
  unsigned int _RendererID=0;
  std::shared_ptr<unsigned int> bufforTest;
  unsigned int *_RendererID_ptr;
  Texture(const std::string &filePath);
  Texture() {};
  Texture(const Texture&) = delete;
  Texture &operator=(const Texture &) = delete;

  Texture(Texture &&other) : _RendererID(other._RendererID)
  {
    other._RendererID = 0; //Use the "null" texture for the old object.
  }

  Texture &operator=(Texture &&other)
  {
    //ALWAYS check for self-assignment.
    if(this != &other)
    {
      this->Release();
      //obj_ is now 0.
      std::swap(_RendererID, other._RendererID);
    }
  }

  ~Texture() { this->Release(); };
  void Bind(unsigned int slot = 0) const;
  void Unbind() const;

  inline int GetWidth() const { return this->_width; }
  inline int GetHeight() const { return this->_height; }
};

Zatem wygląda na to że miałem napisany źle copy, oraz asigment constructor oraz destruktory. Według Khronos :

This happens because we violated C++'s rule of 3/5: if you write for a class one of a destructor, copy/move constructor, or copy/move assignment operator, then you must write all of them.

The compiler-generated copy constructor is wrong; it copies the OpenGL object name, not the OpenGL object itself. This leaves two C++ objects which each intend to destroy the same OpenGL object.

Ideally, copying a RAII wrapper should cause a copy of the OpenGL object's data into a new OpenGL object. This would leave each C++ object with its own unique OpenGL object. However, copying an OpenGL object's data to a new object is incredibly expensive; it is also essentially impossible to do, thanks to the ability of extensions to add state that you might not statically know about.

So instead, we should forbid copying of OpenGL wrapper objects. Such types should be move-only types; on move, we steal the resource from the moved-from object.

Projekt wrzucam w całości na github: https://github.com/toch88/OpenGl_Plot/tree/ResourceMenager, zapraszam do dalszych komentarzy.

komentarz 7 kwietnia 2018 przez j23 Mędrzec (194,920 p.)

że miałem napisany źle copy, oraz asigment constructor

Przecież konstruktora kopiującego i operatora przypisania nie ma (zablokowałeś oba). Texture może być tylko przeniesiona. Problem w tym, że konstruktor przenoszący i operator nie przenoszą wszystkiego, jedynie _RendererID.

komentarz 7 kwietnia 2018 przez Mateusz Tocha Bywalec (2,560 p.)
edycja 7 kwietnia 2018 przez Mateusz Tocha
Klasa bedzie tylko "movable" i nie będzię możliwe kopiowanie. Teraz to działa segmentation fault znikło. Czy masz na myśli coś innego?
komentarz 7 kwietnia 2018 przez j23 Mędrzec (194,920 p.)

No mam na myśli to, że konstruktor i operator powinny przenieść jeszcze pola _FilePath, _LocalBuffer, _width, _height, _BPP, bufforTest i _RendererID_ptr.

 

 

komentarz 7 kwietnia 2018 przez Mateusz Tocha Bywalec (2,560 p.)

Racja, dziękuje za sugestie, rozwiązałem to w trochę bardziej dla mnie czytelny sposób.

class TextureWrapper
{
  private:
    void Release()
    {
        glDeleteTextures(1, &_RendererID);
        _RendererID = 0;
    }

  public:
    unsigned int _RendererID = 0;    
    TextureWrapper(){};
    TextureWrapper(const TextureWrapper &) = delete;
    TextureWrapper &operator=(const TextureWrapper &) = delete;

    TextureWrapper(TextureWrapper &&other) : _RendererID(other._RendererID)
    {
        other._RendererID = 0; //Use the "null" TextureWrapper for the old object.
    }

    TextureWrapper &operator=(TextureWrapper &&other)
    {
        //ALWAYS check for self-assignment.
        if (this != &other)
        {
            this->Release();
            //obj_ is now 0.
            std::swap(_RendererID, other._RendererID);
        }
    }

    ~TextureWrapper() { 
        this->Release();
     };
};


class Texture
{
private:
  std::string _FilePath;
  unsigned char *_LocalBuffer;
  int _width, _height, _BPP; 

public:
  unsigned int _RendererID=0;
  TextureWrapper texture;
  
  Texture(const std::string &filePath);
  Texture() {};  
 
  void Bind(unsigned int slot = 0) const;
  void Unbind() const;

  inline int GetWidth() const { return this->_width; }
  inline int GetHeight() const { return this->_height; }
};

 

Podobne pytania

+1 głos
1 odpowiedź 211 wizyt
pytanie zadane 17 lutego 2022 w C i C++ przez Yaqbek Nowicjusz (160 p.)
0 głosów
0 odpowiedzi 125 wizyt
0 głosów
0 odpowiedzi 867 wizyt

92,576 zapytań

141,426 odpowiedzi

319,652 komentarzy

61,961 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...