Кресты / Говнокод #20091 Ссылка на оригинал

0

  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 Bobik, (Updated )

Комментарии (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?
        Ответить
            • Она в buf как была так и осталась :3

              new ничего нового не выделял, просто прокастовал указатель на buf и вызвал конструктор. По сути, new(buf) SomeClass - просто вызов конструктора на указанном куске памяти.
              Ответить
          • > освобождение памяти, выделенной на стеке
            Даже так: освобождение через 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;
              Ответить
          • Начало буфера может криво лечь, без учёта требований на выравнивание полей. И на 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.
            Ответить
        • для кастомных алокаторов памяти, например. Выделяешь память под дофига объектов разом, делаешь placement new по мере необходимости, потом прогоняешь деструктор и разом освобождаешь
          Ответить
        • > Можно для контейнеров

          использование в контейнерах это частный случай аллокатора так то
          Ответить
    • ничего не сурово. я так часто пишу (с 3rd party классами/библиотеками, с STL такого не делал) что бы не заставлять народ в код лезть искать какой именно перегруженый оператор вызывается.
      Ответить
        • я бы еще понял что-то вроде
          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). Поэтому его специализация тут не нужна, тем более такая
                  Ответить

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

Где здесь C++, guest?!

    А не использовать ли нам bbcode?


    8