Проблемы со статической инициализацией - PullRequest
0 голосов
/ 08 мая 2009

У меня странные проблемы со статической инициализацией. Я использую генератор кода для создания структур и кода сериализации для системы передачи сообщений, которую я написал. Чтобы можно было легко распределять сообщения на основе их идентификатора, у меня есть вывод кода моего генератора кода, похожий на следующий для каждого типа сообщения:

MessageAllocator s_InputPushUserControllerMessageAlloc(INPUT_PUSH_USER_CONTROLLER_MESSAGE_ID, (AllocateMessageFunc)Create_InputPushUserControllerMessage);

Класс MessageAllocator в основном выглядит следующим образом:

MessageAllocator::MessageAllocator( uint32_t messageTypeID, AllocateMessageFunc func )
{
    if (!s_map) s_map = new std::map<uint32_t, AllocateMessageFunc>();
    if (s_map->insert(std::make_pair(messageTypeID, func)).second == false)
    {
        //duplicate key!
        ASSERT(false, L"Nooooo!");
    }
    s_count++;
}

MessageAllocator::~MessageAllocator()
{
    s_count--;
    if (s_count == 0) delete s_map;
}

, где s _ map и s _ count являются статическими членами MessageAllocator. Это работает большую часть времени, но иногда сообщения не добавляются на карту. Например, это конкретное сообщение не добавляется, пока я не вызову Create _ InputPushUserControllerMessage () где-нибудь в моем коде запуска, однако другие сообщения работают нормально. Я подумал, что это может быть связано с неправильным компоновщиком, думая, что тип не имеет ссылки, и удалил его, поэтому я отключил это с помощью параметра / OPT: NOREF (я использую Visual Studio 2008 SP1), но это не дало никакого эффекта.

Мне известно о проблеме «фиаско статического порядка инициализации», но, насколько мне известно, порядок, в котором создаются эти объекты, не должен изменять результат, поэтому мне это кажется нормальным.

Любое понимание здесь будет оценено.

Ответы [ 4 ]

2 голосов
/ 08 мая 2009

Поместить статическое в класс, чтобы он был статическим членом класса

struct InputPushUserControllerMessageAlloc { static MessageAllocator s_obj; };

MessageAllocator InputPushUserControllerMessageAlloc::s_obj(
    INPUT_PUSH_USER_CONTROLLER_MESSAGE_ID, 
    (AllocateMessageFunc)Create_InputPushUserControllerMessage); 

Стандарт позволяет задерживать инициализацию объектов, имеющих область имен, до тех пор, пока не будет использована какая-либо функция / объект из ее единицы перевода. Если инициализация имеет побочный эффект, она не может быть оптимизирована. Но это не запрещает откладывать это.

Не так для объектов, имеющих область видимости. Так что это может запретить оптимизировать что-то там.

1 голос
/ 08 мая 2009

Я бы изменил s_map из статического члена класса в статический член метода:

std::map<uint32_t,AllocateMessageFunc>& MessageAllocator::getMap()
{
    // Initialized on first use and destroyed correctly on program termination.
    static std::map<uint32_t,AllocateMessageFunc>   s_map;

    return s_map;
}
MessageAllocator::MessageAllocator( uint32_t messageTypeID, AllocateMessageFunc func )
{
    if (getMap().insert(std::make_pair(messageTypeID, func)).second == false)
    {
         //duplicate key!
         ASSERT(false, L"Nooooo!");
    }
}

Нет необходимости в деструкторе или счете.

Если ваши глобальные объекты находятся в отдельных библиотеках DLL (или общих библиотеках), которые загружаются лениво. Это может вызвать проблему, похожую на ваше описание.

0 голосов
/ 11 мая 2009

Оказывается, что объектные файлы, содержащие статические инициализаторы, не были включены компоновщиком, потому что ничто не ссылалось на какие-либо функции в них. Чтобы обойти это, я извлек одну из сгенерированных «C» функций, чтобы она имела предсказуемое не искаженное имя, а затем принудительно ссылался на нее, используя такую ​​прагму для каждого сообщения

#pragma comment(linker, "/include:Create_GraphicsDynamicMeshCreationMessage")

, который я положил в сгенерированный заголовочный файл, который позже будет включен во все остальные не сгенерированные файлы. Это только MSVC и что-то вроде взлома, но я предполагаю, что смогу сделать что-то похожее на GCC, когда в конце концов перенесу это.

0 голосов
/ 08 мая 2009

Вы не устанавливаете указатель обратно на ноль.

MessageAllocator::~MessageAllocator()
{
    s_count--;
    if (s_count == 0) 
    {
          delete s_map;
          s_map = 0;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...