C++ статическая инициализация
template<class T>
class A
{
public:
static int get_my_problem_value
{
static MyProblem my_problem;
return my_problem.value;
}
};
ps
Если my_problem используется в нескольких местах, то оборачиваешь ее в функцию, которая возвращает ссылку на my_problem.
Проблема такого решения что оно для каких то инстанциаций может не позваться на статике. А в рантайме этот код не потокобезопасный.
Щас пока остановился на том что вкорячил сюда проверку на то была ли статик инициализация или не была. Если не была то мьютекс.
Код получился громоздким и возможно с проблемками, потестю в понедельник.
А в рантайме этот код не потокобезопасный.для 11 плюсов я где-то читал , что безопасный по стандарту
но дефакто он давно уже потокобезопасный (для gcc/llvm по крайней мере)
компилятор вставляет __cxa_guard_acquire / __cxa_guard_release через которые осуществляется синхронизация
Щас пока остановился на том что вкорячил сюда проверку на то была ли статик инициализация или не была. Если не была то мьютекс.кстати, хз подо что ты там пишешь , для x86 можно и так, но в общем случае, либо проверка должна быть под мютексом, либо надо в дополнение очень аккуратно расставлять мемори барьеры/volatile, а это корректно сделать реал не просто
короче, нынче Double-checked locking считается антипатерном потому что его х. кто может реализовать нормально
Щас пока остановился на том что вкорячил сюда проверку на то была ли статик инициализация или не была. Если не была то мьютекс.Ну на тебе немного крипоты в этом треде.
короче, нынче Double-checked locking считается антипатерном потому что его х. кто может реализовать нормальноСцукоапиридил Форте в капсулах.
короче, нынче Double-checked locking считается антипатерном потому что его х. кто может реализовать нормальноВроде не считается. Да и реализовать просто:
std::atomic<T *> ptr;
std::mutex mutex;
T *instance {
if (auto result = ptr.load(std::memory_order_relaxed {
return result;
}
std::lock_guard<std::mutex> lock(mutex);
if (auto result = ptr.load(std::memory_order_relaxed {
return result;
}
auto result = new T;
ptr.store(result, std::memory_order_relaxed);
return result;
}
Все проблемы, описанные в статье Александреску и Майерса, — от отсутствия в 2004 году атомиков и модели памяти для многотредовых C++ программ как таковой.
Но зачем писать всё это, когда можно сделать локальную статическую переменную и заставить компилятор написать всё это за тебя, — непонятно.
auto result = new T;
и
ptr.store(result, std::memory_order_relaxed);
должен быть мемори барьер, т.к. на другом процессоре сторы-инициализации в конструкторе Т могут быть увидены после стора-записи указателя, там сработает проверка ptr!=0 и он полезет к поляи Т до того, как он увидит инициализации этих полей (на x86 такого не может быть, но в общем случае может)
так что ты сейчас только подтвердил, что это антипаттерн
upd
===
и вроде после первого ptr.load тоже надо барьер ставить для полной корректности, остальные барьеры мютекс выставит. Хотя я не уверен на 100% что этого достаточно)
Да, ты прав, load должен быть acquire, store должен быть release. Косячу, однако. :-)
антипаттерн, епты)
Так что задача простая.
Соглашусь с геномом. Если бы на код ревью пропустили, очень велика вероятность, что кому-то пришлось бы потратить 10+ часов, чтобы найти такой root cause.
Но зачем писать всё это, когда можно сделать локальную статическую переменную и заставить компилятор написать всё это за тебя, — непонятно.Ну, собственно, потому и считается. Там, где его можно гарантированно реализовать, нах он сдался. Там, где в нем нужда, он все равно неправильно скомпилируется.
Да ладно, всего со второй попытки правильно получилось.Я был прав, это действительно очевидно. (с)
Так что задача простая.
Да ладно, всего со второй попытки правильно получилось.C++ has indeed become too "expert friendly" (c) Stroustrup
Так что задача простая.
компилятор вставляет __cxa_guard_acquire / __cxa_guard_release через которые осуществляется синхронизацияух ты
вот за это спасибо
пойду смотреть поддерживает ли наш древнючий gcc это
кому интересно вот что я нагородил:
// A.h
template<class T>
class A
{
static std::auto_ptr<MyProblem>* my_problem;
static Mutex mutex;
static bool has_static_init_finished;
static __attribute__ noinline bool _has_static_init_finished
{
return true;
}
static void init
{
static std::auto_ptr<MyProblem> static_my_problem;
static Mutex m_mutex;
static bool has_static_init_finished;
if (!static_my_problem.get
{
static_my_problem.reset(new MyProblem;
_GLIBCXX_WRITE_MEM_BARRIER;
my_problem = &static_my_problem;
}
}
static const MyProblem& get_my_problem
{
// static T my_problem;
// return my_problem;
if (!my_problem)
{
if (has_static_init_finished)
{
Guard<Mutex> guard(mutex);
init;
}
else
{
init;
}
}
return *(my_problem->get;
}
public:
static int get_my_problem_value
{
return get_my_problem.value;
}
};
//template <class T>
//MyProblem A<T>::my_problem;
template <class T>
std::auto_ptr<MyProblem>* A<T>::my_problem;
template <class T>
Mutex A<T>::mutex;
template <class T>
bool A<T>::has_static_init_finished = A<T>::_has_static_init_finished;
// B.cpp
int my_problem_value = A<int>::get_my_problem_value;
int main
{
std::cout << "my_problem_value = " << my_problem_value << "\n";
}
пойду смотреть поддерживает ли наш древнючий gcc этода
поддерживает начиная с 4.0.0
проверил - всё ок
выкинул всё это барахло и оставил просто static local
всем спасибо
Оставить комментарий
pitrik2
Как заиспользовать schwarz counter для статической переменной темплейтного класса?В этом коде стандарт С++ чётко говорит что самое первое что сделается это "A<int>::my_problem.value = 0".
А вот дальше стандарт не определяет порядок инициализации.
По факту у меня получается, что сначала зовётся get_my_problem_value и соотв. возвращает 0, а потом уже зовётся конструктор A<int>::my_problem, который тудыть кладёт 5.
Страуструп говорит, что стандартный способ форсировать порядок статической инициализации это использовать schwarz counter.
Гугл даже предлагает готовую реализацию и пример использования.
Но как же её заиспользовать для шаблонного класса-то?
Я же не могу определить глобальную темплейтную переменную.
Я могу для каждой инстанциации шаблона это сделать, но я же их заранее все не знаю:
Коллеги по работе говорят, что это сделать нельзя. И единственный вариант, чтобы не отказываться от статической инициализации -> это использовать либо синглтон либо локальную статическую переменную внутри функции.