- 01
- 02
- 03
- 04
- 05
- 06
- 07
- 08
- 09
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
class SMemManager {
public:
SMemManager() { std::cout<<"Singleton new: "<<this<<std::endl; init(); }
void* operator new(size_t) { return (void*)p_body; }
void operator delete(void* ) { }
private:
void init() {
if (p_init!=0xF3149A51) {
p_countMemData=0;
p_memData=0;
std::cout<<"Singleton init: "<<this<<std::endl;
}
p_init=0xF3149A51;
}
unsigned int p_init;
unsigned int p_countMemData;
void ** p_memData;
static char p_body[];
};
char SMemManager::p_body[sizeof(SMemManager)];
Один из эпичных синглтонов от foxes.
Посмотреть эволюцию этого шедевра или окунуться в его обсуждение можно в теме по ссылке:
http://www.gamedev.ru/code/forum/?id=211629&page=6#m81
Ещё весело то, что это вообще не синглтон ни разу.
Ну а какой синглтон можно считать православным и чтоб при этом бомжиками не пах? Чаще всего я вижу такой: Но, накушавшись проблем с порядком инициализации, делаю теперь так:http://ideone.com/cFVPiO
А почему не простой вариант с проверкой?
Из-за производительности? Или тоже как-то порядок инициализации ломается? (Хотя, как? Наоборот, он правильнее будет, т.к. зависимые сущности вызовут того, от кого зависеть строго раньше завершения своего создания)
Дополнительным плюсом стало то, что объекты создаются именно в том порядке, в котором они объявлены в коде, т.е. легче трейсить код и читать логи.
Ассерты не решение: их нет в продакшене, отстрелить ногу это всё же даст.
> нужно быть внимательным с очерёдностью удаления
Нунах, пусть за меня RAII работает.
> Ассерты не решение
Всего лишь компилируемость кода в релизе - достаточный критерий для его прохождения в мастер ветку? Ну, пиздец, хотя такие личности мне встречались.
Ладно, хуй с ним, синглетоном. Всем добра и обнимашек!
А если прописать ограничения на работу с синглтоном, то Мейерсовский покатит.
Чего? Синглетон со статической переменной в функции - самый кошерный. Начиная с C++11 он ещё и потокобезопасен из коробки. Инициализация автоматическая будет при первом использовании.
Возможно, ты хотел сказать "порядок уничтожения", а не "порядок инициализации".
На эльбрусе нет 11-х крестов, и не будет ещё очень скоро :) В QNX нейтрино есть лишь частичная поддержка, и та - неофициально.
> Возможно, ты хотел сказать "порядок уничтожения", а не "порядок инициализации".
Я имел ввиду именно порядок инициализации, уже отписался про инициализацию из другого потока.
Кстати, да. Порядок уничтожения тоже нужен вполне определенный. Сходу пример на Qt не сгенерирую, но его суть можно свести к простому правилу: все QObject-ы надо уничтожить до уничтожения QApplication.
Но pthread_once() для защиты конкуррентной инициализации можно юзать уже сейчас
> Порядок уничтожения тоже нужен вполне определенный
Александреску по этому поводу целую секцию написал в "Модном ЦПП Дизайне" полтора десятка лет назад.
Ок. Ты - умный, помнишь этот фрагмент. Я - тупой и не смог прочесть и осознать книгу полностью.
Вот код, приводящий к сегфолту по выходу:
>struct Singletone {
> static Singletone & instance() { static Singletone s; return s; }
>};
Вот я твой код в говнокоды запишу и буду таким же идиотом как -Eugene-.
Но UB здесь всё равно есть т.к. буфер из char'ов не гарантирует достаточного выравнивания для полей данного класса.
Выравнивание адреса p_body это вообще другой вопрос.
Не размер, а выравнивание (alignment). Не на всех платформах void* и int можно читать с кривых адресов. Да и на amd64 может пиздануться если конпелятор надумает заюзать sse.
Ну да, атрибутом на p_body должно решиться.
Разумеется, попытка набрать анскильных «программистов» на «C++» и прикрыть их анскильность опциями компиляторами — это заранее обречённый пердёж в лужу.
Повесился бы.
В те тёмные времена, если мне память не изменяет, Стандарт вообще не упоминал выравнивания, поэтому надеяться можно было только на крепость рук, на руки друга и вбитый крюк, и молиться, чтобы компилятор не подвёл.
Да ладно... Я просто делал такие буфера с запасом и потом поинтер округлял вверх. Некрасиво, но жить можно.
https://ideone.com/wgf1UK
§ 6.9.3.3/8
А есть другие варианты? Какую там опцию надо включить у gcc чтобы он зафорсил всем глобалкам выравнивание на N?
Не работает. Вот такой код со всеми вариантами этой опции криво выровнен на amd64:
А на твоей платформе должно?
Короче я теперь вообще не понимаю что эта опция делает.
Что эта опция вообще делает?
Зачем, зачем? Почему не сделать тип с нужным выравниванием typedef uint8_t uint8_t_align8 __attribute__ ((aligned (8))); допустим
и потом делать глобалки с таким типом ?
Ну и в любом случае, на уровне языка никаких «-O1» нет, поэтому запись в невыровненные переменные всё равно будет UB.
alignof(T) требует, это ID.
Это где ты такое прочитал? https://stackoverflow.com/a/506590
> The alignment has the following guarantee from the standard (3.7.3.1/2):
> The pointer returned shall be suitably aligned so that it can be converted to a pointer of any complete object type and then used to access the object or array in the storage allocated (until the storage is explicitly deallocated by a call to a corresponding deallocation function).
Т.е. выровнено так, чтоб любую хуйню туда норм было писать, если говорить простым языком
§ 7.6.2.7.18, N4842
Вот, в этом и есть вся проблема. А сделать так, чтобы он знал, очень просто:
Вот, теперь ты имеешь полное право записать в q инт.
Ещё правильнее, конечно, использовать std::aligned_storage.
С кодовой базой, которая компилируется только на gcc 3.0 и рефакторингу не подлежит, можно сделать только одну разумную вещь: переместить на дискету, дискету выкинуть в мусорное ведро, мусорное ведро передать уборщице.
Да, именно. Чтобы такой хуйни не было — компилятор с оптимизирующими опциями выравнивает любые переменные — включая массивы байтиков — по размеру слова. Разумеется, менее UB невыровненный доступ от этого не становится.
Всё, что можно сделать с кодом с UB — это превратить его в код с ID, если документация конкретной версии конкретного компилятора явно постулирует, что такое-то поведение определено.
alignas(int) char q[11 + sizeof(int)];
Чтобы получить нормальный код.
А теперь выходить это тоже UB?
На его сайте написано:
> ⚫ 2003 - 2008 г. МГУПИ
Московский государственный университет приборостроения и информатики
(высшее)
Получается что лет 18 назад.
Вот к чему байтоебство приводит.
Какой багор )))
На самом-то деле 10 лет назад.
Про реальную проблему я уже вроде давал ссылку
см. https://govnokod.ru/27068#comment589971 --> https://pzemtsov.github.io/2016/11/06/bug-story-alignment-on-x86.html
Это делается очень просто. Пусть есть код:
На архитектуре, которая может читать только по четыре байта, компилятор положит q по адресу, кратному четырём байтам. А дальше произойдёт магия (псевдоасм, на endianness похуй):
Вуаля: процессор читает только по выровненным адресам, а в printf передаётся только один нужный байт.
Доступ к объекту с невыровненным адресом — это UB по Стандарту C++. Всё, больше никаких рассуждений не требуется: так писать нельзя.
> Иначе говоря такая архитектура гарантирует начало массива Q выровненную надлежащим образом.
Вовсе нет. Компилятор может (и будет, если решит поэкономить место) поместить Q по невыровненному адресу, а операции доступа скорректировать соответствующим образом. Например:
Здесь q вполне может лежать по нечётному адресу. Тогда чтения a и первых трёх элементов q будут генерировать загрузку четырёх байт по адресу &a; чтения элементов q[3]..q[6] — по адресу &q[3].
Мы уже 10 раз определили разными способами как его, практически не меняя, сделать не UB.
Есть такая очень толстая книжка, называется «Стандарт языка C++».
В этой очень толстой книжке, помимо всего прочего, содержатся требования к коду на C++. Если очень упрощать, то в них содержатся утверждения вида «код корректен, если X. Если Y, то код некорректен».
Так вот, в этой книжке написано (опять же, упрощая), что объект типа T может храниться только адресу, выровненному по границе в alignof(T) байт. Если мы попытаемся как-то запихнуть объект типа T в адрес, не выровненный границе в alignof(T), то наш код станет некорректным по Стандарту — то есть, будет содержать UB.
Во всех этих построениях, как ты мог заметить, отсутствует одна важная вещь: упоминание архитектуры. И это неспроста: фишка в том, что, если Стандарт объявляет, что операция X приводит к неопределённому поведению, то она приводит к неопределённому поведению на любой архитектуре, на любом компиляторе и с любым сочетанием директив компиляции.
Все наши рассуждения в этом топике направлены на то, чтобы объяснить, почему исходная операция (доступ по невыровненному адресу) была объявлена Стандартом UB, только и всего.
Вот именно вот это "то мы в жопе.". Неопределенный результат всегда трактуется в пользу архитектуры. Иначе говоря определяется архитектурой. Тоже самое когда встал вопрос про "выкинуть UB или все таки добавить выравнивание".
Нет. Ты путаешь неопределённое поведение и поведение, определяемое имплементацией («Implementation-defined behaviour»).
Ну и причем тут компилятор?
"Но я рад, что вы признали наличие тут "неопределенного результата", ведь это и есть UB!"
unspecified переводиться как неопределенный - только и всего.
Точно также как при сдвиге чисел со знаком пишут, что результат не определен - хотя это совсем не так. Все прекрасно определяется архитектурой.
Не может такого быть, на примере x86. Потому что указатель на p_body однозначно передается на выход new без какого либо преобразования.
Это так, к сожалению. Компилятор на 146% уверен, что сдвиг знакового числа влево не меняет его знак. И если дальше есть какие-то ассёрты или условия про знак числа - он их тупо выбросит. В итоге у тебя потом отрицательное число попадёт в ветку для положительных.
§ 7.6.7
Блин, а когда пофиксили кста? Я вот смотрю, в a * = 2 и a += a переполнение конпелятор считает UB'ом. А с a <<= 1 аккуратно перепроверяет все инварианты после сдвига, наебать не получается.
Скажем так, он не будет заморачиваться чтобы код работал в кейсах, которые он считает некорректными.
Вот пример: https://ideone.com/EFGoBi
Он не про сдвиг, но вполне показывает чем заканчивается undefined behavior. Там вон куча проверок на отрицательность и на вылет символа за границу. И все они ушли на помойку.
Две проверки выкинул!
Типа тут могло бы быть переполнение, но нахуй его?
UPD: Это ж какой потенциал для наёба автотестов!
https://gcc.godbolt.org/z/TEe4sa
Есть. Команды для знакового сдвига (вправо) отличаются от беззнаковых.
Нельзя сдвигать на отрицательное число бит или на число бит, большее, чем размер левого операнда. Всё остальное современный Стандарт разрешает.
> Все прекрасно определяется архитектурой.
И ты опять ничего не понял. В «C++» нет никакой архитектуры, есть только Стандарт. Можно писать код, соответствующий Стандарту — это будет хороший, переносимый код. А можно писать код с UB — и тогда это в самом лучшем случае будет жёстко прибитое к конкретной версии компилятора говно. За такое полагается пинок линейкой из профессии.
Ты опять в очередной раз не может правильно прочитать предложение, и пытаешься на основе этого мне что то заново объяснить. Или ты просто так самоутверждаешься?
Не неопределённый результат, а неопределённое поведение. Иногда из-за UB'а вообще огромный кусок кода пропадает т.к. конпелятор не видит в нём смысла. А иногда начинается пиздец в духе поехавшей логики когда true && true == false.
class foo
{
char a __attribute__ ((packed));
int y __attribute__ ((packed));
};
foo array[100] __attribute__ ((aligned (1)));
Тоже самое можно легко провернуть для инта.
Я пробегусь автозаменой, или напишу говноскрипт какой-нибудь (с огрызком парсера), который это сделает только там, где надо
static int kok2 = petuh(); // выполняется при инициализации.
[1] https://gcode.space#!/
Ваши крестопроблемы с порядком инициализации лишь забавляют меня.
Или конпелятор просто выбросит этот код, т.к. UB...
Круто?
Деструктор обычно делает что-то важное.
деструктор иногда что-то делает
> программы
Чтоб тебе всю жизнь динамически загружаемые модули писать...
Я бы не был так категоричен. К сожалению, есть ресурсы, с которыми только ребут поможет, если забудешь их освободить... Ну и есть ресурсы, которые ломаются к хуям, если их не освободить и завершить прогу. Примеров не будет, подумай над ними сам.
думаешь, ты кому-то тут глаза открыл? Едва ли. Но ресурсами бывает не только память.
> О том, как пофлашить свои данные при завершении, читайте в комментарии немного выше.
> Синхронизировать можно в деструкторе статической переменной-обертки.
Ну да, я же конфетная фабрика, чтобы для каждого куска делать обертку.
И, последнее. Чем твой синглтон лучше чем
https://habrahabr.ru/post/147373/
а на хуя тогда пиздеть?
- Да, здравствуйте, Александр Константинович
http://govnokod.ru/19596#comment315551
купите платный аккаунт
[NSFW!] http://img0.joyreactor.cc/pics/comment/geek-велосипед-вело-гифки-1767996.gif
> Пока я этот код для ржачки не с ляпал
Нашли или выдавили из себя код, который нельзя назвать нормальным, на который без улыбки не взглянешь? Не торопитесь его удалять или рефакторить, — запостите его на говнокод.ру, посмеёмся вместе!
http://govnokod.ru/19596#comment315551
http://govnokod.ru/19596#comment315549
Очень хорошо, загружайте код, посмеёмся вместе.
> ржать над кодом или над моими рассуждениями
Ммм... Ну код виден; видны и рассуждения. Ржать над кодом - значит ржать над некоторыми поверхностными рассуждениями.
> можно поржать над половиной комментариев
Так затем и собираемся здесь. Давайте ржать уже!
давай теперь ржать над этим вместе http://govnokod.ru/19596#comment315399
этот комент http://govnokod.ru/19596#comment315399 и не подписал - во смотрите че пишет, во ржачь.
В принципе все понятно - у колобка нет шеи, и над тем кто так шутит можно по ржать.