1. C++ / Говнокод #19596

    0

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    12. 12
    13. 13
    14. 14
    15. 15
    16. 16
    17. 17
    18. 18
    19. 19
    20. 20
    21. 21
    22. 22
    23. 23
    24. 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

    Запостил: -Eugene-, 09 Марта 2016

    Комментарии (264) RSS

    • А что предполагается делать, если сойдутся звёзды и значении p_init сразу будет 0xF3149A51? Кто ответственен за уничтожение объекта (Бич большей части самопальных синглтонов)?

      Ещё весело то, что это вообще не синглтон ни разу.
      Ответить
      • > Бич большей части самопальных синглтонов
        Ну а какой синглтон можно считать православным и чтоб при этом бомжиками не пах? Чаще всего я вижу такой:
        struct Singletone {
            static Singletone & instance() { static Singletone s; return s; }
        };
        Но, накушавшись проблем с порядком инициализации, делаю теперь так:http://ideone.com/cFVPiO
        Ответить
        • > делаю теперь так
          А почему не простой вариант с проверкой?
          Singletone & Singletone::instance() {
          	if (singlInstance == 0) singlInstance = new Singletone();
          	return *singlInstance;
          }

          Из-за производительности? Или тоже как-то порядок инициализации ломается? (Хотя, как? Наоборот, он правильнее будет, т.к. зависимые сущности вызовут того, от кого зависеть строго раньше завершения своего создания)
          Ответить
          • Такая вот отложенная инициализация использовалась у нас в дремучие времена msvc2003, но возникли проблемы с инициализацией объекта из других потоков.
            Дополнительным плюсом стало то, что объекты создаются именно в том порядке, в котором они объявлены в коде, т.е. легче трейсить код и читать логи.
            Ответить
        • Чтобы решить проблему удаления и не использовать статические объекты, можно зарегистрировать обработчик atexit. При нормальном завершении работы он запустится и удалит объект. В этом случае нужно быть внимательным с очерёдностью удаления. Проблемы с многопоточной инициализацией решаются call_once.

          Ассерты не решение: их нет в продакшене, отстрелить ногу это всё же даст.
          Ответить
          • > atexit
            > нужно быть внимательным с очерёдностью удаления
            Нунах, пусть за меня RAII работает.

            > Ассерты не решение
            Всего лишь компилируемость кода в релизе - достаточный критерий для его прохождения в мастер ветку? Ну, пиздец, хотя такие личности мне встречались.

            Ладно, хуй с ним, синглетоном. Всем добра и обнимашек!
            Ответить
            • Вот с RAII приходится очень осторожным с очерёдностью удаления быть. Дёрнет какой-нибудь статичный объект в деструкторе синглтон и всё. Причём всё может работать, пока какой-нибудь не относящийся к делу код не изменят.

              А если прописать ограничения на работу с синглтоном, то Мейерсовский покатит.
              Ответить
        • > Но, накушавшись проблем с порядком инициализации

          Чего? Синглетон со статической переменной в функции - самый кошерный. Начиная с C++11 он ещё и потокобезопасен из коробки. Инициализация автоматическая будет при первом использовании.

          Возможно, ты хотел сказать "порядок уничтожения", а не "порядок инициализации".
          Ответить
          • > Начиная с C++11
            На эльбрусе нет 11-х крестов, и не будет ещё очень скоро :) В QNX нейтрино есть лишь частичная поддержка, и та - неофициально.

            > Возможно, ты хотел сказать "порядок уничтожения", а не "порядок инициализации".
            Я имел ввиду именно порядок инициализации, уже отписался про инициализацию из другого потока.
            Кстати, да. Порядок уничтожения тоже нужен вполне определенный. Сходу пример на Qt не сгенерирую, но его суть можно свести к простому правилу: все QObject-ы надо уничтожить до уничтожения QApplication.
            Ответить
            • Хороший пример на эту тему -- static QNetworkAccessManager в функции.
              Ответить
            • > и не будет ещё очень скоро
              Но pthread_once() для защиты конкуррентной инициализации можно юзать уже сейчас

              > Порядок уничтожения тоже нужен вполне определенный
              Александреску по этому поводу целую секцию написал в "Модном ЦПП Дизайне" полтора десятка лет назад.
              Ответить
              • > Александреску по этому поводу целую секцию написал
                Ок. Ты - умный, помнишь этот фрагмент. Я - тупой и не смог прочесть и осознать книгу полностью.

                Вот код, приводящий к сегфолту по выходу:
                #include <QApplication>
                #include <QGraphicsView>
                #include <QGraphicsScene>
                #include <QLabel>
                
                QGraphicsScene & scene() {
                    static QGraphicsScene s;
                    return s;
                }
                
                int main(int argc, char *argv[])
                {
                    QApplication a(argc, argv);
                    QGraphicsView w;
                    w.setScene(&scene());
                    scene().addWidget(new QLabel("label"));
                    w.show();
                    return a.exec();
                }
                Ответить
                • лель. Причина краша не в синглтонном QObject'e.

                  #include <QApplication>
                  #include <QGraphicsScene>
                  #include <QGraphicsView>
                  #include <QLabel>
                  #include <QGraphicsPolygonItem>
                  #include <QDebug>
                  
                  QGraphicsScene & scene() {
                      static QGraphicsScene s;
                      return s;
                  }
                  
                  int main(int argc, char *argv[])
                  {
                      QApplication a(argc, argv);
                      QGraphicsView w;
                      w.setScene(&scene());
                      QLabel *label = new QLabel("label");
                      scene().addWidget(label);
                      w.show();
                      int res = a.exec();
                      delete label;
                      return res;
                  }
                  Ответить
        • >Xom94ok
          >struct Singletone {
          > static Singletone & instance() { static Singletone s; return s; }
          >};

          Вот я твой код в говнокоды запишу и буду таким же идиотом как -Eugene-.
          Ответить
          • Ну как, записал?
            Ответить
            • Ты понимаешь что товарищ -Eugene-, выложил код менеджера памяти и присвоил ему мое авторство? Или вообще не догоняешь?
              Ответить
              • Какой Макото Синкай ))) Ответы на срачи в говнокоде идут четыре года...
                Ответить
            • Но это пол беды. Основная беда - это большинство здесь присутствующих, которые называют его UB, даже не понимая его работу. И не имея понятие о стандарте С/С++ где четко говориться, что все статические и глобальные переменные обнуляются до какой либо инициализации. Так же как и присвоенные им значения хранятся в сегменте данных непосредственно в самой переменной еще до запуска программы.
              Ответить
              • Ладно, ладно, невнимательно прочитал код...

                Но UB здесь всё равно есть т.к. буфер из char'ов не гарантирует достаточного выравнивания для полей данного класса.
                Ответить
                • Каким образом не гарантирует? sizeof - не гарантирует его размер? Где такое можно прочитать? Даже если бы SMemManager имел виртуальную таблицу, к его размеру добавился размер ссылки на таблицу виртуальных методов. Или если бы он был составным, также ни каких проблем.
                  Выравнивание адреса p_body это вообще другой вопрос.
                  Ответить
                  • > размер

                    Не размер, а выравнивание (alignment). Не на всех платформах void* и int можно читать с кривых адресов. Да и на amd64 может пиздануться если конпелятор надумает заюзать sse.
                    Ответить
                    • Ты знаешь из каких времен этот код?
                      Ответить
                    • В остальном проблема решается аргументом компилятора, который наверняка установлен по умолчанию на выравнивание. Но это не беда алгоритма.
                      Ответить
                      • > аргументом компилятора

                        Ну да, атрибутом на p_body должно решиться.
                        Ответить
                        • Для старого кода, если таких полей сотни, ты также будешь все поля перечислять?
                          Ответить
                          • Делать надо правильно, а не костыли в аргументы компилятору совать.
                            Ответить
                            • Если стоит выравнивание по умолчанию и разработчики приняли это за данность, то это уже правильно.
                              Ответить
                              • Это непереносимый код. Того, кто без нужды пишет непереносимый код, нужно гнать из профессии линейкой по пальцам.
                                Ответить
                                • Есть целые компании которые вводят подобные правила как внутренний стандарт для упрощения написание программ. Если каждый будет задумываться над выравниванием, это усложнит написание и повысит требования к кандидату а следовательно и зарплату. А сейчас всем нужна дешевая рабочая сила.
                                  Ответить
                                  • Если стоит задача «писать код и не задумываться» — надо набирать JS-макак и программы делать, соответственно, на «JavaScript». И тогда не придётся думать ни про выравнивания, ни про инициализацию, ни про синглтоны: просто пишешь код и радуешься жизни.

                                    Разумеется, попытка набрать анскильных «программистов» на «C++» и прикрыть их анскильность опциями компиляторами — это заранее обречённый пердёж в лужу.
                                    Ответить
                              • показать все, что скрытоvanished
                                Ответить
                                • Не доверяют статическим глобалкам внутри функций (см. вариант Хомячка в первых комментах).
                                  Ответить
                                • Это нужно, чтобы любые обращения к подобному полю не вызвали неоднозначности. Например ты в методе petuh(); вызываешь выделение памяти, а менеджер памяти еще не готов.

                                  https://ideone.com/wgf1UK
                                  Ответить
                                • В современных крестах это нужно только для того, чтобы не заучивать наизусть § 6.9.3.3. Ну и ради этого, пужулуй:
                                  If the initialization of a non-local variable with static or thread storage
                                  duration exits via an exception, the function std::terminate is called
                                  § 6.9.3.3/8
                                  Ответить
                          • > ты также будешь все поля перечислять

                            А есть другие варианты? Какую там опцию надо включить у gcc чтобы он зафорсил всем глобалкам выравнивание на N?
                            Ответить
                            • показать все, что скрытоvanished
                              Ответить
                              • Возможно и есть, но я не видел, поэтому и справшиваю более опытных людей.
                                Ответить
                                • -malign-data
                                  Ответить
                                  • показать все, что скрытоvanished
                                    Ответить
                                  • > -malign-data

                                    Не работает. Вот такой код со всеми вариантами этой опции криво выровнен на amd64:
                                    char anotherVar;         // offset = 0
                                    char shouldBeAligned[4]; // offset = 2 instead of 4
                                    Ответить
                                    • что именно не работает -malign-data=abi, -malign-data=xlen, -malign-data=cachelin, -malign-data=natural

                                      А на твоей платформе должно?
                                      Ответить
                                      • Во всех доступных на x86_64 вариантах (compat, cacheline и abi) shouldBeAligned не выровнен на 4. Если я в этот буфер потом засуну какой-нибудь атомик, то он на некоторых операциях будет немного не атомарный.

                                        Короче я теперь вообще не понимаю что эта опция делает.
                                        Ответить
                                        • Подтверждаю, говно какое-то: https://wandbox.org/permlink/cTU0gpmyuHL3Qgus.
                                          Ответить
                                          • Ну да, я думал с cacheline оно разбросает их подальше, чтобы каждой глобалке свой кешлайн достался и фолс шаринга не было. А фиг там. Видимо для чаров это не работает.

                                            Что эта опция вообще делает?
                                            Ответить
                            • > Какую там опцию надо включить у gcc чтобы он зафорсил всем глобалкам выравнивание на N?

                              Зачем, зачем? Почему не сделать тип с нужным выравниванием typedef uint8_t uint8_t_align8 __attribute__ ((aligned (8))); допустим

                              и потом делать глобалки с таким типом ?
                              Ответить
                              • См. коммент от foxes выше. В коде таких глобалок уже сотни и выправлять их дорого.
                                Ответить
                                • см мой коммент ниже
                                  Ответить
                                • На самом деле -o1 уже включает выравнивание.
                                  Ответить
                                  • Какое именно (по какой границе) выравнивание включает «-O1», выравнивание каких именно сущностей включает «-O1», где в документации написано, что «-O1» гарантирует выравнивание, как изменить границу выравнивания?

                                    Ну и в любом случае, на уровне языка никаких «-O1» нет, поэтому запись в невыровненные переменные всё равно будет UB.
                                    Ответить
                                    • показать все, что скрытоvanished
                                      Ответить
                                      • > а какое требует С++, ктстати?
                                        alignof(T) требует, это ID.
                                        Ответить
                                    • В таком случае вам придется все вами объявленные переменные вручную выравнивать.
                                      Ответить
                                      • показать все, что скрытоvanished
                                        Ответить
                                        • Ну тогда в чем проблема то?
                                          Ответить
                                          • показать все, что скрытоvanished
                                            Ответить
                                            • B что с того? Чтение чара по кривому адресу разрешено, а чтение инта нет - это бред сам по себе. Ни какое sse тут участвовать не может - просто компилятор не даст, а в специальных компиляторах в таких случаях используется выравнивание по умолчанию. Даже o1 выравнивает адрес не зависимо от типа данных.
                                              Ответить
                                          • В том, что записывать в q инты нельзя.
                                            Ответить
                                            • Это какая то несуществующая проблема, которая была создана для разведения срача.
                                              Ответить
                                              • Это прямое нарушение Стандарта, про который ты так неосмотрительно упомянул в начале дискуссии.
                                                Ответить
                                                • Какого стандарта? Оператор new выделяет память как угодно и не обязательно выравнено по границе адресов, и уж точно не соотносит выделение памяти с размером объекта. Так что не надо тут в уши лить.
                                                  Ответить
                                                  • > Оператор new выделяет память как угодно и не обязательно выравнено по границе адресов

                                                    Это где ты такое прочитал? 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).

                                                    Т.е. выровнено так, чтоб любую хуйню туда норм было писать, если говорить простым языком
                                                    Ответить
                                                  • Смешно, и это говорит человек, что-то говоривший про Стандарт.
                                                    Overload resolution is performed on a function call created
                                                    by assembling an argument list. The first argument is the amount
                                                    of space requested, and has type std::size_t. If the type of
                                                    the allocated object has new-extended alignment, the next argument
                                                    is the type’s alignment, and has type std::align_val_t. [...]

                                                    § 7.6.2.7.18, N4842
                                                    Ответить
                                              • Не, вообще проблема вполне реальная, я про такую хуйню недавно упоминал, вот там https://govnokod.ru/27068#comment589971 даже ссылка есть на реальный пример проявления этой проблемы на x86-64 связанный с SSE.
                                                Ответить
                                                • Так он связан с SSE! Сюда то он как привязался?
                                                  Ответить
                                                  • Так, что компилятор может (имеет право) сгенерить SSE инструкцию для записи-чтения по каким-то адресам. И если адреса невыровнены, у тебя будет ошибка в процессе исполнения.
                                                    Ответить
                                                    • Не имеет он такого права, а если ему такие права даются, то и адреса он в таком случае обязан выравнивать у всех переменных соответственно, а не по типу char.
                                                      Ответить
                                                      • Он имеет полное право так делать при записи-чтении по указателю типа T*. Потому что указатель типа T* обязан быть выровнен по границе alignof(T) байт.
                                                        Ответить
                                                      • показать все, что скрытоvanished
                                                        Ответить
                                                        • Ни откуда он не знает! Если вы говорите про архитектуру то тут не может быть ни каких типов выравнивания.
                                                          Ответить
                                                          • > Ни откуда он не знает!
                                                            Вот, в этом и есть вся проблема. А сделать так, чтобы он знал, очень просто:
                                                            alignas(int) char q[11 + sizeof(int)];

                                                            Вот, теперь ты имеешь полное право записать в q инт.
                                                            Ещё правильнее, конечно, использовать std::aligned_storage.
                                                            Ответить
                                                            • На компилятора gcc 3.0 работает?
                                                              Ответить
                                                              • А что, в 2020-м году есть хоть малейший смысл писать на C++03?
                                                                Ответить
                                                                • В gcc 3.0 можно срать в литералы! В последующих эту фичу выпилили :(
                                                                  Ответить
                                                                • Видимо есть, раз вы все это пишете под кодом, который даже не знаете для чего и зачем написан.
                                                                  Ответить
                                                                  • На самом деле, разумеется, нет.
                                                                    С кодовой базой, которая компилируется только на gcc 3.0 и рефакторингу не подлежит, можно сделать только одну разумную вещь: переместить на дискету, дискету выкинуть в мусорное ведро, мусорное ведро передать уборщице.
                                                                    Ответить
                                        • Да, переменные выравниваются как надо. А вот как только мы делаем вот так:
                                          int *qi = reinterpret_cast<int*>(q);
                                          — мы получаем UB, потому что адрес q не обязан быть выровнен по alignof(int).
                                          Ответить
                                          • показать все, что скрытоvanished
                                            Ответить
                                          • Если требуется выровненные адреса в архитектуре то он должен выравниваться по одному шаблону, а не по int или char.
                                            Ответить
                                            • Вовсе нет. У любого типа в «C++» есть так называемые «alignment requirements», и эти требования совершенно не обязаны совпадать между собой (они и не совпадают).
                                              Ответить
                                              • Вы понимаете что вы путаете одно с другим! Если архитектура не может считывать по кривому адресу, то такое выравнивание не будет работать.
                                                Ответить
                                                • показать все, что скрытоvanished
                                                  Ответить
                                                  • Как только реально столкнетесь с этой проблемой, а не со сферическим конем в вакууме, дайте знать. А пока вы даже не знаете написан ли он для ARM архитектуры или только для одной x86 - 486.
                                                    Ответить
                                                  • Кажется, foxes не понимает, как можно считать один байт по кривому адресу в архитектуре (CPU), которая не поддерживает невыровненное чтение.
                                                    Это делается очень просто. Пусть есть код:
                                                    char q[8] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'};
                                                    printf("%c", q[6])

                                                    На архитектуре, которая может читать только по четыре байта, компилятор положит q по адресу, кратному четырём байтам. А дальше произойдёт магия (псевдоасм, на endianness похуй):
                                                    MOV edi, DWORD PTR [q + 4]
                                                    AND edi, 0x0000FF00
                                                    SHL edi, 1
                                                    ; ...

                                                    Вуаля: процессор читает только по выровненным адресам, а в printf передаётся только один нужный байт.
                                                    Ответить
                                                    • показать все, что скрытоvanished
                                                      Ответить
                                                    • Иначе говоря такая архитектура гарантирует начало массива Q выровненную надлежащим образом. А значит объект имеющий адрес массива Q не будет иметь ни каких проблем которые вы тут перечисляете.
                                                      Ответить
                                                      • А причём тут архитектура процессора, если мы про код с UB?
                                                        Доступ к объекту с невыровненным адресом — это UB по Стандарту C++. Всё, больше никаких рассуждений не требуется: так писать нельзя.

                                                        > Иначе говоря такая архитектура гарантирует начало массива Q выровненную надлежащим образом.
                                                        Вовсе нет. Компилятор может (и будет, если решит поэкономить место) поместить Q по невыровненному адресу, а операции доступа скорректировать соответствующим образом. Например:
                                                        char a;
                                                        char q[11];

                                                        Здесь q вполне может лежать по нечётному адресу. Тогда чтения a и первых трёх элементов q будут генерировать загрузку четырёх байт по адресу &a; чтения элементов q[3]..q[6] — по адресу &q[3].
                                                        Ответить
                                                        • Не я начал с примера архитектуры на котором этот код может быть ub.
                                                          Мы уже 10 раз определили разными способами как его, практически не меняя, сделать не UB.
                                                          Ответить
                                                          • показать все, что скрытоvanished
                                                            Ответить
                                                            • Вы сейчас также как и -Eugene-, сейчас собираетесь все эти вещи мне присвоить.
                                                              Ответить
                                                          • Ты плохо понимаешь, что такое «UB», объясню попроще.

                                                            Есть такая очень толстая книжка, называется «Стандарт языка C++».
                                                            В этой очень толстой книжке, помимо всего прочего, содержатся требования к коду на C++. Если очень упрощать, то в них содержатся утверждения вида «код корректен, если X. Если Y, то код некорректен».
                                                            Так вот, в этой книжке написано (опять же, упрощая), что объект типа T может храниться только адресу, выровненному по границе в alignof(T) байт. Если мы попытаемся как-то запихнуть объект типа T в адрес, не выровненный границе в alignof(T), то наш код станет некорректным по Стандарту — то есть, будет содержать UB.

                                                            Во всех этих построениях, как ты мог заметить, отсутствует одна важная вещь: упоминание архитектуры. И это неспроста: фишка в том, что, если Стандарт объявляет, что операция X приводит к неопределённому поведению, то она приводит к неопределённому поведению на любой архитектуре, на любом компиляторе и с любым сочетанием директив компиляции.

                                                            Все наши рассуждения в этом топике направлены на то, чтобы объяснить, почему исходная операция (доступ по невыровненному адресу) была объявлена Стандартом UB, только и всего.
                                                            Ответить
                                                            • Ну вы зря потеряли время. Поскольку я уже не первый раз пишу о том, что все эти оговорки в стандарте не всегда правильно трактуются. И их надо рассматривать комплексно, а не выдергивать фразы из контекста.
                                                              Ответить
                                                              • показать все, что скрытоvanished
                                                                Ответить
                                                                • показать все, что скрытоvanished
                                                                  Ответить
                                                                  • В GNU расширении это было хуй знает когда, а крестостандартизаторы только сейчас внедрили.
                                                                    Ответить
                                                                • "Что тут можно "неверно трактовать"?"
                                                                  Вот именно вот это "то мы в жопе.". Неопределенный результат всегда трактуется в пользу архитектуры. Иначе говоря определяется архитектурой. Тоже самое когда встал вопрос про "выкинуть UB или все таки добавить выравнивание".
                                                                  Ответить
                                                                  • > Неопределенный результат всегда трактуется в пользу архитектуры.
                                                                    Нет. Ты путаешь неопределённое поведение и поведение, определяемое имплементацией («Implementation-defined behaviour»).
                                                                    Ответить
                                                                  • показать все, что скрытоvanished
                                                                    Ответить
                                                                    • "Если бы это было так, то компилятору было бы трудно что-то оптимизировать."
                                                                      Ну и причем тут компилятор?

                                                                      "Но я рад, что вы признали наличие тут "неопределенного результата", ведь это и есть UB!"
                                                                      unspecified переводиться как неопределенный - только и всего.
                                                                      Точно также как при сдвиге чисел со знаком пишут, что результат не определен - хотя это совсем не так. Все прекрасно определяется архитектурой.
                                                                      Ответить
                                                                      • показать все, что скрытоvanished
                                                                        Ответить
                                                                        • "Верно. Получившийся в итоге поинтер может указывать на что угодно. Например, на null. Чтож тут хорошего?"
                                                                          Не может такого быть, на примере x86. Потому что указатель на p_body однозначно передается на выход new без какого либо преобразования.
                                                                          Ответить
                                                                      • > при сдвиге чисел со знаком пишут, что результат не определен - хотя это совсем не так

                                                                        Это так, к сожалению. Компилятор на 146% уверен, что сдвиг знакового числа влево не меняет его знак. И если дальше есть какие-то ассёрты или условия про знак числа - он их тупо выбросит. В итоге у тебя потом отрицательное число попадёт в ветку для положительных.
                                                                        Ответить
                                                                        • Это пофиксили, к щ-щастью.
                                                                          [...] The behavior is undefined if the right operand is negative,
                                                                          or greater than or equal to the width of the promoted left operand.
                                                                          
                                                                          2 The value of E1 << E2 is the unique value congruent to
                                                                          E1 × 2^E2 modulo 2^N, where N is the width of the type of the result.
                                                                          [Note: E1 is left-shifted E2 bit positions; vacated bits are zero-filled. —end note]
                                                                          3 The value of E1 >> E2 is E1/2^E2, rounded down. [Note: E1 is right-shifted
                                                                          E2 bit positions. Right-shift on signed integral types is an arithmetic
                                                                          right shift, which performs sign-extension. —end note]

                                                                          § 7.6.7
                                                                          Ответить
                                                                          • > Это пофиксили, к щ-щастью.

                                                                            Блин, а когда пофиксили кста? Я вот смотрю, в a * = 2 и a += a переполнение конпелятор считает UB'ом. А с a <<= 1 аккуратно перепроверяет все инварианты после сдвига, наебать не получается.
                                                                            Ответить
                                                                            • Хуй знает, по-моему, в C++14 или 17.
                                                                              Ответить
                                                                              • Х.з., я даже на очень старых гцц из четвёртой серии не могу воспроизвести.
                                                                                Ответить
                                                                        • На всех архитектурах присутствует метод сдвига знаковых типов. Но компилятор его выкинет?
                                                                          Ответить
                                                                          • > компилятор его выкинет

                                                                            Скажем так, он не будет заморачиваться чтобы код работал в кейсах, которые он считает некорректными.

                                                                            Вот пример: https://ideone.com/EFGoBi

                                                                            Он не про сдвиг, но вполне показывает чем заканчивается undefined behavior. Там вон куча проверок на отрицательность и на вылет символа за границу. И все они ушли на помойку.
                                                                            Ответить
                                                                            • Помотив: https://wandbox.org/permlink/1113PcNud6DRjZqF.
                                                                              #include <cstdio>
                                                                              #include <cstdlib>
                                                                              
                                                                              int abs_int(int x) {
                                                                                  if (x < 0) {
                                                                                      return -x;   
                                                                                  } else {
                                                                                      return x;   
                                                                                  }
                                                                              }
                                                                              
                                                                              int main()
                                                                              {
                                                                                  int x = 0;
                                                                                  scanf("%d", &x);
                                                                                  
                                                                                  int y = abs_int(x);
                                                                                  if (y < 0) {
                                                                                      printf("y < 0; y = %d\n", y); 
                                                                                  } else {
                                                                                      if (y >= 0) {
                                                                                          printf("y >= 0; y = %d\n", y);
                                                                                      }
                                                                                  }
                                                                                  return EXIT_SUCCESS;
                                                                              }

                                                                              Две проверки выкинул!
                                                                              Ответить
                                                                              • return -x;

                                                                                Типа тут могло бы быть переполнение, но нахуй его?
                                                                                Ответить
                                                                                • Это пресловутое число Тараса за яйца кусает. Его отрицать нельзя. Программисты на «C++» — умные ребята, UB не делают, и компилятор им верит. А раз программу писали умные ребята и UB в ней нет, то и abs_int() всегда возвращает положительное число: на все последующие проверки можно положить!
                                                                                  Ответить
                                                                                • Кстати, если вместо scanf() напрямую записать -2147483648 в x, то выводится честное «y < 0; y = -2147483648». Какое UB )))

                                                                                  UPD: Это ж какой потенциал для наёба автотестов!
                                                                                  Ответить
                                                                            • Поднимем градус безумия и докажем, что x == y:
                                                                              bool is_equal(int x, int y)
                                                                              {
                                                                                  for (;;) {
                                                                                      if (x == y) {
                                                                                          return true;
                                                                                      }
                                                                                  }
                                                                                  return false;
                                                                              }

                                                                              https://gcc.godbolt.org/z/TEe4sa
                                                                              Ответить
                                                                              • Я так теорему ферма опровергал.
                                                                                Ответить
                                                                                • Есть в этом что-то философское: если искать вещь бесконечно долго, то она обязательно найдётся.
                                                                                  Ответить
                                                                          • показать все, что скрытоvanished
                                                                            Ответить
                                                                            • > в архитектуре есть понятие знаковости

                                                                              Есть. Команды для знакового сдвига (вправо) отличаются от беззнаковых.
                                                                              Ответить
                                                                              • показать все, что скрытоvanished
                                                                                Ответить
                                                                                • Нет везде есть. Если быть точным есть 3-4 типа сдвигов. Но конкретно в знаковых типах есть нюанс - команда сдвига влево такая же как и у без знакового, из-за нее знак терялся. А точнее знак соответствовал биту 31-n, из за чего результат трактовался неопределенным.
                                                                                  Ответить
                                                                          • Ответить
                                                                      • > Точно также как при сдвиге чисел со знаком пишут, что результат не определен
                                                                        Нельзя сдвигать на отрицательное число бит или на число бит, большее, чем размер левого операнда. Всё остальное современный Стандарт разрешает.

                                                                        > Все прекрасно определяется архитектурой.
                                                                        И ты опять ничего не понял. В «C++» нет никакой архитектуры, есть только Стандарт. Можно писать код, соответствующий Стандарту — это будет хороший, переносимый код. А можно писать код с UB — и тогда это в самом лучшем случае будет жёстко прибитое к конкретной версии компилятора говно. За такое полагается пинок линейкой из профессии.
                                                                        Ответить
                                                                        • "Нельзя сдвигать на отрицательное число бит или на число бит,"
                                                                          Ты опять в очередной раз не может правильно прочитать предложение, и пытаешься на основе этого мне что то заново объяснить. Или ты просто так самоутверждаешься?
                                                                          Ответить
                                                                          • Утверждение в твоём предложении не соответствовало действительности, я лишь поправил тебя. Сдвигать числа со знаком современный Стандарт разрешает.
                                                                            Ответить
                                                                            • Остается дождаться когда указатели определят.
                                                                              Ответить
                                                                              • Да, когда определят — будет хорошо.
                                                                                Ответить
                                                                  • > Неопределенный результат

                                                                    Не неопределённый результат, а неопределённое поведение. Иногда из-за UB'а вообще огромный кусок кода пропадает т.к. конпелятор не видит в нём смысла. А иногда начинается пиздец в духе поехавшей логики когда true && true == false.
                                                                    Ответить
                                                        • Читая все это можно сделать выводы, что подобный код тоже UB.

                                                          class foo
                                                          {
                                                          char a __attribute__ ((packed));
                                                          int y __attribute__ ((packed));
                                                          };

                                                          foo array[100] __attribute__ ((aligned (1)));
                                                          Ответить
                                                    • "Вуаля: процессор читает только по выровненным адресам, а в printf передаётся только один нужный байт."
                                                      Тоже самое можно легко провернуть для инта.
                                                      Ответить
                                      • Какое именно (по какой границе) выравнивание включает «-O1», выравнивание каких именно сущностей включает «-O1», где в документации написано, что «-O1» гарантирует выравнивание?
                                        Ответить
                          • > ты также будешь все поля перечислять?

                            Я пробегусь автозаменой, или напишу говноскрипт какой-нибудь (с огрызком парсера), который это сделает только там, где надо
                            Ответить
              • показать все, что скрытоvanished
                Ответить
                • показать все, что скрытоvanished
                  Ответить
                  • Не совсем. Там несколько фаз есть. Нули и constexpr'ы сразу при загрузке образа заполнены. Потом динамическая инициализация (как в твоём примере). И только потом main.
                    Ответить
                  • static int kok1 = 10; // храниться в сегменте данных программы, в теле исполняемого файла
                    static int kok2 = petuh(); // выполняется при инициализации.
                    Ответить
            • Господа некроманты, предлагаю в ваши заклинания добавлять ссылку на нгк [1], чтобы поднятые могли быстрее влиться в современность.

              [1] https://gcode.space#!/
              Ответить
        • > Но, накушавшись проблем с порядком инициализации

          Ваши крестопроблемы с порядком инициализации лишь забавляют меня.
          Ответить
          • В крестах хотя бы порядок инициализации глобальных пельменных определён.
            Ответить
      • Вообще если предположить что такой код действительно мной используется, то начнем с того, что не выделение не удаления не происходит, чтобы вы не вызывали. и инит тут выступает только за инициализацию содержимого .
        Ответить
    • > значении p_init сразу будет 0xF3149A51
      Или конпелятор просто выбросит этот код, т.к. UB...
      Ответить
    • А можно синглтоны вообще не удалять. И никаких проблем с порядком.
      X &X::get() {
          static X *instance = new X;
          return *instance;
      }

      Круто?
      Ответить
      • Ага, особенно круто, когда этот синглтон держит какой-либо буфер, который нужно синхронизировать с физическим носителем. А потом получается "почему программа не сохраняет последние несколько минут работы"
        Ответить
        • Синхронизировать можно в деструкторе статической переменной-обертки. А сам синглтон оставить на куче и не трогать. Только синхронизация чего-то в деструкторах - это уже какой-то говнокод.
          Ответить
        • Из-за вот такой хуйни я и узнал, что в шарпе тоже надо диспозить иногда
          Ответить
      • очень круто, но только если в твоем синглтоне нет деструктора или nested-классов с деструкторами
        Ответить
        • И че будет, если есть деструктор?
          Ответить
          • он не вызовется.
            Ответить
            • В этом смысл. Что плохого-то?
              Ответить
              • > Что плохого-то?
                Деструктор обычно делает что-то важное.
                Ответить
                • не совсем так. Деструктор иногда делает что-то кроме удаления своих полей.
                  Ответить
                  • Ок, давай ещё корректней скажем:
                    деструктор иногда что-то делает
                    Ответить
              • в том, что любые классы, работающие с io девайсами нельзя удалять через free
                Ответить
              • Вот вы дураки. Выгляните немного за рамки своих карго-правил. Речь идет о вызове деструктора при завершении программы. Любой ресурс и так будет освобожден и без деструктора. О том, как пофлашить свои данные при завершении, читайте в комментарии немного выше.
                Ответить
                • > при завершении программы
                  > программы
                  Чтоб тебе всю жизнь динамически загружаемые модули писать...
                  Ответить
                • > Любой ресурс
                  Я бы не был так категоричен. К сожалению, есть ресурсы, с которыми только ребут поможет, если забудешь их освободить... Ну и есть ресурсы, которые ломаются к хуям, если их не освободить и завершить прогу. Примеров не будет, подумай над ними сам.
                  Ответить
                • > Речь идет о вызове деструктора при завершении программы

                  думаешь, ты кому-то тут глаза открыл? Едва ли. Но ресурсами бывает не только память.

                  > О том, как пофлашить свои данные при завершении, читайте в комментарии немного выше.
                  > Синхронизировать можно в деструкторе статической переменной-обертки.

                  Ну да, я же конфетная фабрика, чтобы для каждого куска делать обертку.

                  И, последнее. Чем твой синглтон лучше чем
                  static SingletonClass *instance() {
                      static std::shared_ptr<SingletonClass> instance(new SingletonClass);
                      return instance;
                  }
                  Ответить
                  • Что это ты тут наговнокодил? Иди уроки делать!
                    Ответить
                • показать все, что скрытоvanished
                  Ответить
    • Да чувак, ты реально дауна кусок. Пока я этот код для ржачки не с ляпал, ни у одного и в голову не пришло хоть какую то реализацию сделать. И мозгами подумать. Шедевров там и без меня хватает.
      Ответить
      • >ни у одного и в голову не пришло хоть какую то реализацию сделать
        https://habrahabr.ru/post/147373/
        Ответить
        • а у меня в репозитории их тысячи и таких же евгенов с говнокодом. Тоже ссылку дать?
          Ответить
          • конечно.
            Ответить
            • вперед www.workstation.pro/svn
              Ответить
              • авторизацию требует
                Ответить
                • ну извини, доступ к военпрому для всех не завозили.
                  Ответить
                  • >а у меня в репозитории их тысячи и таких же евгенов с говнокодом. Тоже ссылку дать?

                    а на хуя тогда пиздеть?
                    Ответить
              • - Здравствуйте, это анонимная линия говнокода?
                - Да, здравствуйте, Александр Константинович
                Ответить
                • и вам привет.
                  http://govnokod.ru/19596#comment315551
                  Ответить
                • http://govnokod.ru/19596#comment315549
                  Ответить
                  • у меня ссылки не кликабельны
                    купите платный аккаунт
                    Ответить
                    • Скрипты - и даже аватарки становятся кликабельными...
                      Ответить
                      • плюс на винте появляется 10 гигабайт самого свежего постоянно обновляемого порно.
                        Ответить
                        • Скрипты воруют пароли. У меня вот угнали и приходится с гостя сидеть.
                          Ответить
                          • Да, бывает такое. Помню, написал скрипт, а он через 10 минут пароль от мыла угнал, всех контактов в мессенджере обматерил и купил на все деньги на счету хуёв резиновых. Со скриптами осторожным нужно быть, да.
                            Ответить
                    • Тебе продать мышь с правой кнопкой?
                      Ответить
                      • Да она грязная небось, с помойки.
                        Ответить
                        • Опять вам по второму разу повторять все, мудакам? У нас не валят в одну мусорку биомусор и компы. Так что протрешь немного и будет заебись.
                          Ответить
                          • Пидор, почему ты такой злой?
                            Ответить
                            • Потому, что у него велосипеда нет.
                              [NSFW!] http://img0.joyreactor.cc/pics/comment/geek-велосипед-вело-гифки-1767996.gif
                              Ответить
                              • http://vignette3.wikia.nocookie.net/southpark/images/1/15/ITSP.jpg/revision/latest?cb=20090205190005
                                Ответить
                • А теперь танцуем!
                  Ответить
      • И что? Это не говнокод? Или лицензия не позволяет цитировать его даже со ссылкой?

        > Пока я этот код для ржачки не с ляпал
        Нашли или выдавили из себя код, который нельзя назвать нормальным, на который без улыбки не взглянешь? Не торопитесь его удалять или рефакторить, — запостите его на говнокод.ру, посмеёмся вместе!
        Ответить
        • без проблем
          http://govnokod.ru/19596#comment315551
          http://govnokod.ru/19596#comment315549
          Ответить
        • Разницу чувствуешь - ржать над кодом или над моими рассуждениями.
          Ответить
        • а в реальности, можно поржать над половиной комментариев из того что здесь, под этим кодом написанно.
          Ответить
          • > comment315551
            Очень хорошо, загружайте код, посмеёмся вместе.

            > ржать над кодом или над моими рассуждениями
            Ммм... Ну код виден; видны и рассуждения. Ржать над кодом - значит ржать над некоторыми поверхностными рассуждениями.

            > можно поржать над половиной комментариев
            Так затем и собираемся здесь. Давайте ржать уже!
            Ответить
            • так вот я и ржу: пишешь шутку - колобок повесился... А тебе говорят - вот ты чудик как же он может повеситься он же колобок, у него шеи нет - хахаха...

              давай теперь ржать над этим вместе http://govnokod.ru/19596#comment315399
              Ответить
              • А когда другой человек репостнул шутку на профильный сайт, обязательно нужно бомбить в комментариях и закидывать людей какашками?
                Ответить
                • например? я пока вижу только твои какашки.
                  Ответить
                  • Можешь перечитать свои комментарии, если не помнишь..
                    Ответить
                    • я все перечитал и единственную подобную глупость которую я не сделал на пару с тобой это не репостил
                      этот комент http://govnokod.ru/19596#comment315399 и не подписал - во смотрите че пишет, во ржачь.

                      В принципе все понятно - у колобка нет шеи, и над тем кто так шутит можно по ржать.
                      Ответить
                      • Чувствую, что кому-то в этом треде нужна асбестовая подушка...
                        Ответить
                        • Да, я вообще кроме такого кода ни чего не пишу, если что сразу обращайтесь ко мне - видно почерк автора.
                          Ответить
                        • http://govnokod.ru/19603
                          Ответить
                        • https://m.youtube.com/watch?v=5KGHd6szj-s
                          Ответить
    • После этой публикации перепишу весь свой код с синглтонами именно на эту реализацию.
      Ответить
    • http://www.demoscene.ru/forum/viewtopic.php?t=892
      Ответить

    Добавить комментарий