На главную
ПРАВИЛА FAQ Помощь Участники Календарь Избранное DigiMania RSS
msm.ru
Модераторы: JoeUser, Qraizer, Hsilgos
  
> Проблема при перемещении файлов исходного кода, Появилась ошибка при выполнении
    ОС: Windows
    Компилятор: Идущий по стандарту с VS2012

    Возникла интересная проблема (по крайней мере для меня).
    Есть проект из ~20 классов.
    Все исходники располагались в одном каталоге, но я решил разложить их по подкаталогам.
    Всё сделал, исправил пути в директивах включения, но проект начал выдавать ошибку доступа к памяти при самом запуске.
    ExpandedWrap disabled
      Первый этап обработки исключения по адресу 0x009DD0A4 в SG.exe: 0xC0000005: нарушение прав доступа при чтении по адресу 0x00000004.
      Если для этого исключения имеется обработчик, выполнение программы может быть продолжено безопасно.

    В общем, перекопал проект и нашёл один класс, от положения которого в дереве каталогов зависит эта ошибка.

    Класс - реализующий паттерн "Фабрика".
    Класс был сделан основываясь на фабрику, описанную в книге Александреску "Современное проектирование на C++".
    Но в моём случае, вместо синглтона используется класс со статическими полями и методами (по сути статический класс).

    Перемещаю файл исходного кода и заголовочный файл в корень ("src"), всё работает, перемещаю в "src/Engine/Modules" - получаю ошибку.

    Проблема скорее всего в регистрации фабрик актёров (классы которые создаются) в ActorFFactory. Потому как, если закомментировать все регистрации (последний листинг), то ошибка пропадает. Да и пробовал вставлять отладочный метод в registerActor перед return. Метод срабатывает.
    Ошибка появляется если регистрируется любой актёр.

    Проверял: дело вероятнее всего не в включаемых в ActorFactory.h заголовках и точно не в отладочных методах.

    Листинги.

    ActorFactory.h
    ExpandedWrap disabled
      #ifndef ACTOR_FACTORY
      #define ACTOR_FACTORY
       
      #include <map>
       
      #include "../../Globals.h"
       
      // dummy for debugging methods
      #if (!defined DEBUG) && (!defined RELEASE)
          #define RELEASE
          #define WARN(message)
          #define WARN_IF(condition, message)
      #endif
       
      #include "../Core/IActor.h"
      #include "../Core/Vector2D.h"
      #include "../Core/Rotator.h"
      #include "../Core/World.h"
       
      /**
       * Factory that allows to create any type of registered actor by their name identifier.
       *
       * Based on pattern Factory in book Andrei Alexandrescu "Modern C++ Design: Generic Programming and Design Patterns Applied"
       */
      class ActorFactory
      {
      public:
          typedef IActor* (*CreateActorCallback)(World *world, const Vector2D location, const Vector2D scale, const Rotator rotation);
      private:
          typedef std::map<std::string, CreateActorCallback> CallbackMap;
      public:
          /**
           * register new actor class by identifier and "create" function
           * @return true if class was not registered earlier
           */
          static bool registerActor(std::string actorId, CreateActorCallback createFn);
       
          /**
           * unregister already registred class
           * @return true if class was unregistered
           */
          static bool unregisterActor(std::string actorID);
       
          /**
           * Create actor by identifier.
           */
          static IActor* createActor(std::string actorID, World *world, const Vector2D location, const Vector2D size, const Rotator rotation);
      private:
          static CallbackMap callbacks;
      };
       
      #endif


    ActorFactory.cpp
    ExpandedWrap disabled
      #include "ActorFactory.h"
       
      ActorFactory::CallbackMap ActorFactory::callbacks = CallbackMap();
       
      bool ActorFactory::registerActor(std::string actorId, CreateActorCallback createFn)
      {
          return ActorFactory::callbacks.insert(CallbackMap::value_type(actorId, createFn)).second;
      }
       
      bool ActorFactory::unregisterActor(std::string actorID)
      {
          return ActorFactory::callbacks.erase(actorID) == 1;
      }
       
      IActor* ActorFactory::createActor(std::string actorID, World *world, const Vector2D location, const Vector2D scale, const Rotator rotation)
      {
          CallbackMap::const_iterator it = ActorFactory::callbacks.find(actorID);
          
          if (it == ActorFactory::callbacks.end())
          {
              throw std::runtime_error("Unknown actor identefier");
          }
       
          return (it->second)(world, location, scale, rotation);
      }


    А вот такого вида код вставляется в *.cpp файл каждого актёра, который можно будет создать с помощью фабрики.
    ExpandedWrap disabled
      #include "../../Engine/Modules/ActorFactory.h"
       
      // unnamed namespase to hide from another places
      namespace
      {
          // specific factory
          IActor* CreateMan(World *world, const Vector2D location, const Vector2D scale, const Rotator rotation)
          {
              return new Man(world, location);
          }
       
          const std::string MAN_ID = "Man";
       
          // register specific factory in actor factory
          const bool registered = ActorFactory::registerActor(MAN_ID, CreateMan);
      }


    Если есть необходимость, могу поделиться всеми исходниками.

    Иерархия включений для класса ActorFactory
    Прикреплённая картинка
    Прикреплённая картинка
    Сообщение отредактировано: WhyNot -
      Обычно такие вещи происходят из-за нарушения порядка инициализации объектов со static storage duration. У тебя нет неявных зависимостей от такого порядка? Если есть, следует убрать или гарантировать требуемый порядок инициализации.

      Добавлено
      Смотри, что лежит по адресу 0x009DD0A4. Наверняка этот код пробует добраться до поля класса по нульпоинтеру на него.
      Одни с годами умнеют, другие становятся старше.
        Цитата
        Смотри, что лежит по адресу 0x009DD0A4. Наверняка этот код пробует добраться до поля класса по нульпоинтеру на него.

        Как бы посмотреть это?

        Цитата
        Обычно такие вещи происходят из-за нарушения порядка инициализации объектов со static storage duration. У тебя нет неявных зависимостей от такого порядка? Если есть, следует убрать или гарантировать требуемый порядок инициализации.

        Не уверен. Почитал про static storage duration, но пока не до конца понял в каких случаях может быть такая зависимость.

        Пока что, сделал следующее, нашёл всё в проекте, что имеет модификатор static и исключил этот и зависимый код (кроме вышеприведённых полей и методов ActorFactory). Всё так же, в одном месте работает, в другом ошибка.

        В работе с ActorFactory может быть такая проблема?

        Если есть допустим класс "Man". (см конец первого поста)
        Man.cpp включает заголовочный файл ActorFactory.h и тут-же вызывает ActorFactory::registerActor.
        Может же получиться, что вызов ActorFactory::registerActor будет совершён перед тем как этот метод проинициализирован?

        Я вообще правильно понял возможную проблему?

        ---

        Стоит синглтон попробовать применить? Тут пишут что он решает проблему.
        Я от него отказался, потому как посчитал слишком сложным (с точки зрения восприятия кода) решением.
        Сообщение отредактировано: WhyNot -
          Цитата WhyNot @
          Как бы посмотреть это?
          Отладчиком, естественно.
          Цитата WhyNot @
          очитал про static storage duration, но пока не до конца понял в каких случаях может быть такая зависимость.
          Простейший случай:
          ExpandedWrap disabled
            // 1.cpp
            std::vector<int> vec;
             
            // 2.cpp
            extern std::vector<int> vec;
            int top = vec.front();
          Ровно как в той статье. Порядок инициализации не определён для разных единиц трансляции. Поэтому метод может быть вызван раньше, чем отработает конструктор. У тебя скорее всего именно это и происходит. Раньше работало, потому что везло, линкер компоновал записи в "правильном" порядке, например алфавитном. Теперь после растасовки по каталогам они компонуются (то же в алфавитном) в другом порядке.
          Одни с годами умнеют, другие становятся старше.
            Увы дебаггер в VS у меня не работает, с режимом Debug не работает одна из подключаемых библиотек.

            Всё таки сделал ActorFactory синглтоном. В книге Александреску тоже было упомянуто, о том, что он решает проблему с порядком инициализации.

            Ошибка и вправду пропала.
            Судя по всему, можно было вместо этого сделать callbacks указателем и инициализировать его при первом вызове любой функции, которая с ним работает.

            Вот что получилось.
            ExpandedWrap disabled
              // ---------- ActorFactory.h ----------
              #ifndef ACTOR_FACTORY
              #define ACTOR_FACTORY
               
              #include <map>
              #include "../Core/World.h"
               
              /**
               * Factory that allows to create any type of registered actor by their name identifier.
               *
               * Based on pattern Factory in book Andrei Alexandrescu "Modern C++ Design: Generic Programming and Design Patterns Applied"
               *
               * ActorFactory is a singleton.
               */
              class ActorFactory
              {
              public:
                  typedef IActor* (*CreateActorCallback)(World *world, const Vector2D location, const Vector2D scale, const Rotator rotation);
              private:
                  typedef std::map<std::string, CreateActorCallback> CallbackMap;
              public:
                  /**
                   * @return the single instance of the ActorFactory
                   */
                  static ActorFactory& Factory();
               
                  /**
                   * register new actor class by an identifier and a "create" function
                   * @return true if class was not registered earlier
                   */
                  bool registerActor(std::string actorId, CreateActorCallback createFn);
               
                  /**
                   * unregister already registred class
                   * @return true if class was unregistered
                   */
                  bool unregisterActor(std::string actorID);
               
                  /**
                   * Create actor by identifier.
                   */
                  IActor* createActor(std::string actorID, World *world, const Vector2D location, const Vector2D size, const Rotator rotation);
              private:
                  CallbackMap callbacks;
               
                  static ActorFactory* singleInstance;
               
                  /*
                   * Turn off unusable operations
                   */
                  ActorFactory();
                  ActorFactory(const ActorFactory&);
                  ~ActorFactory();
                  ActorFactory& operator=(const ActorFactory&);
              };
               
              #endif
               
               
              // ---------- ActorFactory.cpp ----------
              #include "ActorFactory.h"
               
              ActorFactory* ActorFactory::singleInstance = NULL;
               
              ActorFactory::ActorFactory() {}
              ActorFactory::ActorFactory(const ActorFactory&) {}
              ActorFactory::~ActorFactory() {}
              ActorFactory& ActorFactory::operator=(const ActorFactory&)
              {
                  return *ActorFactory::singleInstance;
              };
               
              ActorFactory& ActorFactory::Factory()
              {
                  if (ActorFactory::singleInstance == NULL)
                  {
                      ActorFactory::singleInstance = new ActorFactory();
                  }
               
                  return *ActorFactory::singleInstance;
              }
               
              bool ActorFactory::registerActor(std::string actorId, CreateActorCallback createFn)
              {
                  return this->callbacks.insert(CallbackMap::value_type(actorId, createFn)).second;
              }
               
              bool ActorFactory::unregisterActor(std::string actorID)
              {
                  return this->callbacks.erase(actorID) == 1;
              }
               
              IActor* ActorFactory::createActor(std::string actorID, World *world, const Vector2D location, const Vector2D scale, const Rotator rotation)
              {
                  CallbackMap::const_iterator it = this->callbacks.find(actorID);
                  
                  if (it == this->callbacks.end())
                  {
                      throw std::runtime_error("Unknown actor identefier");
                  }
               
                  return (it->second)(world, location, scale, rotation);
              }
            0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
            0 пользователей:


            Рейтинг@Mail.ru
            [ Script Execution time: 0,1374 ]   [ 18 queries used ]   [ Generated: 15.12.17, 06:12 GMT ]