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

    +2

    1. 1
    2. 2
    3. 3
    4. 4
    5. 5
    6. 6
    CharT getline(std::istream& i, string& s, const CharT* delim) {
    ...
        if (!i.operator void*()) 
            break;
    ...
    }

    Библиотека Apache UIMA-CPP.
    Что могло заставить написать так, вместо обычного if (i)? Какой-то древний компилятор, который не использует каст к указателю в условии?
    Ну и, разумеется, в C++11 ios::operator void*() заменили на explicit ios::operator bool(), так что работать перестало.

    Запостил: Bobik, 29 Мая 2016

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

    • Ого, вызов оператора по названию. Сурово.
      Ответить
      • И да: что-то я не врубаюсь, i - ссылка на istream, так нахуя её на null проверять?
        Ответить
        • Ссылку проверять на null нельзя. Написать if (i) это всё равно что написать if (!i.fail()).
          Ответить
      • > вызов оператора по названию
        А как тебе вызов деструктора по названию?
        char buf[sizeof(SomeClass)];
        SomeClass* ptr = new(buf) SomeClass;
        // work with ptr
        // ...
        ptr->~SomeClass();
        Ответить
        • Это вообще Челябинск.
          Кстати, а что будет, если потом сделать delete ptr?
          Ответить
          • UB. Аллокатор же об этом блоке не знает.
            Ответить
            • И в результате память утекла безвозвратно?
              Ответить
              • Она в buf как была так и осталась :3

                new ничего нового не выделял, просто прокастовал указатель на buf и вызвал конструктор. По сути, new(buf) SomeClass - просто вызов конструктора на указанном куске памяти.
                Ответить
              • это так называемый placement new, он же "создай вон там"
                Ответить
          • освобождение памяти, выделенной на стеке - UB.
            Ответить
            • > освобождение памяти, выделенной на стеке
              Даже так: освобождение через delete чего-то, что не вернуло new - UB.
              delete (void*)0xDEADBEEF; // вроде не на стеке :3
              Ответить
              • Тащемта сам стек могли аллоцировать с помощью new. Мне кажется, что при определенном соглашении о вызове и определенной фазе луны ptr может указывать на начало стека.
                Ответить
              • откуда ты знаешь что не на стеке?
                очень может быть что на стеке
                просто другого потока например
                Ответить
              • ну по факту delete ptr эквивалентен ptr->~Type(); free(ptr); Просто с точки зрения стандарта нет никакого смысла явно говорить что именно так и должно быть (вдруг кто-то когда-то что-то умнее придумает)
                Ответить
                • Ну там всё-таки не сразу free, а сначала перегружаемый operator delete, который может (но не обязан) вызвать free.
                  Ответить
              • > Даже так: освобождение через delete чего-то, что не вернуло new - UB.
                auto a = new NeUb();
                auto b = std::prev(std::next(a));
                delete b;
                Ответить
        • Это тоже UB, buf не выровнен.
          Ответить
          • Согласен.
            Ответить
          • схуяли чего бы это? Просто неоптимизированно будет.
            Ответить
            • Начало буфера может криво лечь, без учёта требований на выравнивание полей. И на ARM'ах от этого всё по пизде пойдёт, они очень придирчивы к выравниванию. На интеле с некоторыми SSE инструкциями - тоже.
              Ответить
              • В нормальных процах за невыровненый доступ дают exception. И только x86 просто тормозит.
                Ответить
                • Есть команды которые медленнее, но им пох на выравнивание, есть которые быстрее, но кидают исключение.
                  Ответить
                • x86 тоже может экцепшн. Но его бесполезно включать - ибо прилетает на первом же strcpy.
                  Ответить
                  • Схуле?
                    Ответить
                    • Потому что все эти strcpy оптимизированные под x86 и читают блоками по 4+ байта, а на выравнивание этих блоков ложат хуй (т.к. проверка выключена).
                      Ответить
                  • правда?
                    а как?
                    Ответить
                    • Гугли флаг AC (alignment check). Работает вроде только в ring 3 (или в 1..3, не помню).
                      Ответить
                      • эх


                        сколько в интеле всего, чем никто не пользуется
                        Ответить
          • Что значит "не выровнен"?
            Ответить
            • (intptr_t)&buf может не делиться на alignof(SomeClass) нацело
              Ответить
            • На некоторых процессорах существует поверье, что обращаться к нечётным адресам — плохая примета. Ну типа как в европейской традиции живым дарят нечётное количество цветов, а покойникам — чётное. Только тут наоборот.
              Ответить
              • при чем тут поверье? Так процессора читают/пишут в память. Если у меня, допустим, int лежит по адресу ptr=(N+0.5)*sizeof(int), то чтение/запись этого инта будет происходить неатомарно: вместо int i = *(int*)(N*sizeof(int)); будет int i = *(int*)(N*sizeof(int)) << sizeof(int)*4 + *(int*)((N+1)*sizeof(int)) >> sizeof(int)*4;
                Ответить
                • > то чтение/запись этого инта будет происходить неатомарно

                  на интеле. на всех остальных процах, прога с валится с радостным сообщением о кривом выравнивании адреса. потому что по умолчанию strict alignment проверка включена на большинстве процов (даже арм уже дошёл до уровня имения этого флага).
                  Ответить
                  • > валится
                    Если бы валилось... На арме молча возвращалось "повёрнутое" слово.
                    Ответить
                • Я зелёный цвет специально не включил, чтобы все задумались.

                  В общем, копирование данных с адреса, не кратного машинному слову, может быть не реализовано в процессоре физически: просто схема не соберётся. Эмулировать ли это копирование, используя «микрооперации», или отвечать исключением — это дело процессора.

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

                      Например в классическом 8086 проц читал два байта (16 бит, оно же машынное слово). Когда он просил у контроллера памяти считать байты 0x1 и 0x2 то первый попадал в одну часть регистра (например AL) а второй в другую (AH). Соответственно считать машинное слово 0x1 легко.

                      А вот попробуй считать слово начиная с адреса 0x2 (0x2 и 0x3). Придется сначала считать 0x1 и 0x2, потом 0x2 переложить из AH в AL, затем считать 0x3 0x4, затем откинуть 0x4.

                      Долго и нудно.

                      С тех пор много говна утекло, контроллеры памяти теперь читают данные из DIMM сразу по 64 бита, контроллер кеша (который между ядром и контроллером памяти) старается заполнить всю кеш-линейку (которая может быть еще больше), но проблема в целом осталась: если ты читаешь N байт, и перехлестываешь некоторую границу, то читать нужно 2 раза, а не 1. И это усложняет
                      Ответить
        • Интересно, явный вызов деструктора может для чего-нибудь использоваться? Можно для контейнеров, но там нагляднее использовать пару new (void *)/delete (void *)...
          Ответить
          • Супер-паттерн оператора присваивания
            MyClass::operator=(const MyClass& other) {
                this->~MyClass();
                new(this) MyClass(other);
            }


            Placement delete вроде нет.
            Ответить
            • А, я перепутал. В контейнерах вызывается деструктор.
              Placement delete вручную вызвать нельзя.
              Он вызывается, если в конструкторе выбрасывается исключение - вызывается delete, соответствующий по сигнатуре использованному оператору new.
              Ответить
            • Objective C custom setter какой-то
              Ответить
          • для кастомных алокаторов памяти, например. Выделяешь память под дофига объектов разом, делаешь placement new по мере необходимости, потом прогоняешь деструктор и разом освобождаешь
            Ответить
          • > Можно для контейнеров

            использование в контейнерах это частный случай аллокатора так то
            Ответить
      • ничего не сурово. я так часто пишу (с 3rd party классами/библиотеками, с STL такого не делал) что бы не заставлять народ в код лезть искать какой именно перегруженый оператор вызывается.
        Ответить
        • a.operator +(b)
          Ответить
          • я бы еще понял что-то вроде
            a.operator + <Type>(b)

            Хотя, может еще быть operator + (a,b);
            Ответить
            • bool operator!=(const My &other) const { return !operator==(other); }
              bool operator!=(const My &other) const { return !(*this == other); }
              Ответить
              • > !operator==(other)
                Охлол, это отдельным говнокодом надо как while(x --> 0).
                Бедный парсер.
                Ответить
                • А че не так?
                  Ответить
                  • Да просто странно выглядящая конструкция, которую интуитивно читаешь как !A == B.
                    К "a.operator +(b)" вроде уже привык, а такое я ещё не видел и удивился.
                    Ответить
                  • дефолтный оператор != вернет return !(a == b). Поэтому его специализация тут не нужна, тем более такая
                    Ответить
                    • Какой дефолтный оператор? Конпелятор не делает дефолтных операторов !=.
                      Ответить
                • потому что правильно 0 <=-- х
                  Ответить
    • Тоже так делал, но с оператором=
      Ответить

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