Информация об изменениях

Сообщение Re: Продлить жизнь статической переменной в функции от 10.09.2022 11:31

Изменено 10.09.2022 15:44 rg45

Re: Продлить жизнь статической переменной в функции
Здравствуйте, Went, Вы писали:

W>Здравствуйте. Хочется странного.

  Есть функция — геттер синглтона
W>
W>Singleton& get_singleton()
W>{
W>  static Singleton s_singleton;
W>  return s_singleton;
W>}
W>

W>По первому запросу объект создается, при разрушении статики — удаляется. Все хорошо.
W>Но иногда возникает ситуация, при которой происходят попытки обратиться к этому синглтону уже в процессе разрушения статики, и, иногда, уже после того, как переменная s_singleton разрушена.
W>Что делать? Я готов даже создавать новый экземпляр на куче или возвращать nullptr (допустим, функция возвращает указатель), но как надёжно определить тот факт, что статическая переменная уже разрушена? Можно поставить какой-то guard, который будет при разрушении ставить какую-то статическую переменную в особое состояние, но где гарантия, что дебагер, разрушая эту переменную, не затрёт её каким-то 0xDEADBEEF?

Если наш синглтон thread_local, то все танцы, связанные с синхронизацией отпадают. Тогда, думаю, достаточно будет просто сделать чуть более хитрую обертку (можно шаблонную):

http://coliru.stacked-crooked.com/a/36fdd30ff945e647

#include <iostream>

template <typename T>
T* get_singleton();

template <typename T>
class SingletonGuard
{
private:
    SingletonGuard() = default;
    SingletonGuard(const SingletonGuard&) = delete;
    ~SingletonGuard() { m_self_ptr = nullptr; };


    friend T* get_singleton<T>();

    T m_data{};
    static thread_local SingletonGuard m;
    static thread_local SingletonGuard* m_self_ptr;
};

template <typename T>
thread_local SingletonGuard<T> SingletonGuard<T>::m;

template <typename T>
thread_local SingletonGuard<T>* SingletonGuard<T>::m_self_ptr = &m;


template <typename T>
T* get_singleton()
{
    // Такая строгая проверка практически сводит к нулю вероятность использования "мертвого" указателя
    // на тот случай, если статическая память затирается каким-то специальным значением после использования.    
    return SingletonGuard<T>::m_self_ptr == &SingletonGuard<T>::m ? &SingletonGuard<T>::m.m_data : nullptr;    
}

int main()
{
    std::cout << get_singleton<int>() << std::endl;
}
Re: Продлить жизнь статической переменной в функции
Здравствуйте, Went, Вы писали:

W>Здравствуйте. Хочется странного.

  Есть функция — геттер синглтона
W>
W>Singleton& get_singleton()
W>{
W>  static Singleton s_singleton;
W>  return s_singleton;
W>}
W>

W>По первому запросу объект создается, при разрушении статики — удаляется. Все хорошо.
W>Но иногда возникает ситуация, при которой происходят попытки обратиться к этому синглтону уже в процессе разрушения статики, и, иногда, уже после того, как переменная s_singleton разрушена.
W>Что делать? Я готов даже создавать новый экземпляр на куче или возвращать nullptr (допустим, функция возвращает указатель), но как надёжно определить тот факт, что статическая переменная уже разрушена? Можно поставить какой-то guard, который будет при разрушении ставить какую-то статическую переменную в особое состояние, но где гарантия, что дебагер, разрушая эту переменную, не затрёт её каким-то 0xDEADBEEF?

Если наш синглтон thread_local, то все танцы, связанные с синхронизацией отпадают. Тогда, думаю, достаточно будет просто сделать чуть более хитрую обертку (можно шаблонную):

http://coliru.stacked-crooked.com/a/36fdd30ff945e647

#include <iostream>

template <typename T>
T* get_singleton();

template <typename T>
class SingletonGuard
{
private:
    SingletonGuard() = default;
    SingletonGuard(const SingletonGuard&) = delete;
    ~SingletonGuard() { m_self_ptr = nullptr; };


    friend T* get_singleton<T>();

    T m_data{};
    static thread_local SingletonGuard m;
    static thread_local SingletonGuard* m_self_ptr;
};

template <typename T>
thread_local SingletonGuard<T> SingletonGuard<T>::m;

template <typename T>
thread_local SingletonGuard<T>* SingletonGuard<T>::m_self_ptr = &m;


template <typename T>
T* get_singleton()
{
    // Такая строгая проверка практически сводит к нулю вероятность использования "мертвого" указателя
    // на тот случай, если статическая память затирается каким-то специальным значением после использования.    
    return SingletonGuard<T>::m_self_ptr == &SingletonGuard<T>::m ? &SingletonGuard<T>::m.m_data : nullptr; 
    // Хотя, стого говоря, чтение значения из m_self_ptr, после окончания времени его жизни - это UB.
}

int main()
{
    std::cout << get_singleton<int>() << std::endl;
}