1. Java / Говнокод #12629

    +142

    1. 1
    2. 2
    if(!true) // это - не говнокод. это - заглушка.
                    return false;

    Оправдывается ещё )

    Запостил: pitch, 21 Февраля 2013

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

    • Где тут Java, pitch?!
      Напомнило: http://blog.llvm.org/2011/05/what-every-c-programmer-should-know_14.html
      Ответить
      • а че так в java не пишут?
        Ответить
      • > Напомнило: http://blog.llvm.org/2011/05/what-every-c-programmer-should-know_14.html
        Спасибо за ссылку. Красивая фраза там в конце третьей части - C migrated from being a "low level systems programming language that was a tiny layer above PDP assembly" to being a "low level systems programming language, trying to provide decent performance by breaking many people's expectations.
        Ответить
        • что в Си, что в Си++ довольно примитивный синтаксис, непонятно, откуда все эти "breaking many people's expectations" вылазят
          Ответить
          • > довольно примитивный синтаксис
            Слабо написать парсер довольно примитивного синтаксиса? ;)

            А грабли то, в основном, не синтаксические, а логические. Ради того, чтобы компилятору и его разработчику было свободнее, в стандарте рассована куча UB'ов и ID'ов. А почти любой UB - потенциальный разрыв шаблона для того, кто сталкивается с ним в первый раз, ну разве что кроме самых очевидных, до которых можно самому догадаться. Да и даже во вполне определенных областях есть грабли - например если int a = -5 и unsigned b = 10, то что вернет a<b? А чему равна 1/2? А еще до с++11 не было никакого понятия о тредах, все это отдавалось на откуп реализации, со всеми вытекающими последствиями...

            Пример - разадресация NULL'а. В жабошарпике это определенное поведение - вброс экцепшена. В сишке же все думают, что должен быть краш\сегфолт и т.п. Но на самом деле это UB, и может случиться все что угодно, от нормального продолжения работы до краша.
            Ответить
            • >Слабо написать парсер довольно примитивного синтаксиса? ;)
              Сравнил мягкое место с пальцем. Синтаксис сложен для парсера, но для человека он не так сложен. Это как человеку раз плянуть распознать на картинке пару объектов, но алгоритмически над этим ломают годы уже десятилетия, как же тупой компьютер научить распознавать объекты без предварительного брутфорса и огромного количества ошибок.

              >сравнение int и unsigned int
              >В сишке же все думают, что должен быть краш\сегфолт и т.п. Но на самом деле это UB
              По-моему, это довольно очевидные вещи из разряда "здравый смысл". Алсо, это всё corner cases, с которыми вполне можно жить.
              Ответить
              • > но для человека он не так сложен
                Относительно большинства многих других языков с++ как раз таки сложен. Как синтаксисом так и семантикой. Что-то даже не приходит на вскидку пример языка, который бы оказался сложнее с++ по этим характеристикам. Поможете?

                > По-моему, это довольно очевидные вещи из разряда "здравый смысл".
                Ну дык. Если иметь 2-3 года опыта в c/с++, когда-либо сталкиваться с похожей проблемой или где-то о ней прочесть, и смотреть на эту проблему на маленьком куске кода - то все очевидно...

                Вот реальный случай из проекта моего друга: проект компилируется без ошибок и предупреждений, но вылетает посреди работы. valgrind показывает доступ за границы выделенной памяти в конструкторе некого класса. Догадаетесь ли вы с первого раза, что в проект закралась лишняя ашка, в которой описана обрезанная версия класса, видя которую new выделяет намного меньше памяти чем надо?

                Если в программе нарушено правило aliasing'а указателей, здравый смысл чем-то сможет помочь? Или все-таки придется почитать о нём?

                С++ очень мощный язык, и мне нравится как сам язык, так и его возможности. Но я считаю, что в то же время этот язык сложный, и мест для прострела ноги в нем вполне хватит каждому. И здесь очень много вещей, которые надо знать, т.к. догадываться придется долго, или они будут работать на одном компиляторе, но внезапно будут вести себя по-другому на другой архитектуре\компиляторе\опциях.
                Ответить
                • >Если в программе нарушено правило aliasing'а указателей

                  Автоматическое распараллеливание не сработает? Ну не всегда это нужно, а догадаться можно.
                  Ответить
                  • > Автоматическое распараллеливание не сработает?
                    Холодно, здравый смысл не всегда помогает с сишкой ;)

                    Там все гораздо веселее, вот, почитай: http://dbp-consulting.com/StrictAliasing.pdf
                    Ответить
                    • Я вот ушел читать и надеюсь, что компиляторы современные не считают без особой нужды что-то слишком оптимистичное и сторят в память указатели почаще, если не смогли определить полного отсутствие алиасов указателя, а то не люблю баги на пустом месте? У меня если что под x86 есть кеш и для АРМ я не пишу, чтобы ловить армапроблему load hit store.
                      Ответить
                    • Я так понял багов в своих прогрммах я не оберусь? А как же всякие int_ptr_t, uint_ptr_t, size_t, ptr_diff_t? Почему не учавствуют в алиасинге? Я вот указатели на такие штуки люблю. Похоже мой код говно.

                      А если я записываю в область памяти и там переменных нет, но указатели-аласы имеют указатели разных типов и не принадлежат к спску правил, то я опять же получу UB?
                      Ответить
                      • Если я ни в чем не затупил - алиасинг типов отличающихся только знаковостью разрешен. Т.е. size_t* в ptrdiff_t* кастовать можно. И unsigned int* в int* тоже. Иначе было бы совсем печально.
                        Ответить
                        • А кто сказал, что size_t и ptrdiff_t отличаются только знаковостью? Стандарт таких гарантий не дает насколько я помню. В любом случае все очень скользко
                          Ответить
                          • > А кто сказал, что size_t и ptrdiff_t отличаются только знаковостью?
                            Эм, а разве они по стандарту не одинакового размера, к тому же равного размеру указателя?
                            Ответить
                    • http://metropolitant.com/wp-content/uploads/2012/11/jackychan.png
                      Боль! Вообщем они снова придумали с каждым разом все более ожидаемую хуйню.

                      Правильно ли я понял, пользоваться union ом для разруливания в С++ нельзя (только в си), так как он не дает гарантий по стандарту? Приходится копировать память, чтобы это разрулить туда и обратно? Хорошая оптимизация.
                      Ответить
                      • Давайте погадаем что появится в следующем стандарте С++?

                        По стандарту С++24 компилятор крестов обязан приставлять к яйцам Colt Python .357 Magnum. Он должен делать это на растоянии не больше чем 0.11 метра до цели и не ближе 0.90, чтобы жертва не почувствовала и не пришла в смятение раньше времени. Следует также маскировать его под синие розы. При каждом использовании UB, нажимать на спусковой крючок с задержкой не более implementation defined. Если же жертва сама случайно нажмет на спусковой крючок - это UB (но мы то с вами знаем что будет).
                        Ответить
                      • Да на самом деле не так часто в крестах нужно заниматься этими кастами чего попало во что попало. В основном это какие-нибудь небольшие байтоебства для сериализации или вариантных типов.

                        А с memcpy компиляторы неплохо расправляются, к примеру gcc вот это:
                        int32_t swp(int32_t x) {
                            int16_t tmp[2];
                            memcpy(tmp, &x, 4);
                            std::swap(tmp[0], tmp[1]);
                            memcpy(&x, tmp, 4);
                            return x;
                        }
                        Превращает вот в это:
                        _Z3swpi:
                                movl    4(%esp), %eax
                                roll    $16, %eax
                                ret
                        Ответить
                        • Оптимизатор оптимизатору рознь.
                          Ответить
                          • > Оптимизатор оптимизатору рознь.
                            Это да. А компиляторы с/с++, особенно gcc, идут путем замены предсказуемости в одних ситуациях на скорость в других...

                            Забавно, но чем извращенней байтоебство, чем больше оно давало профита раньше, тем хуже оно работает с нынешними оптимизаторами.
                            Ответить
            • >если int a = -5 и unsigned b = 10, то что вернет a<b?
              a проапгрейдится до unsigned и станет например 0хFFFFFFFB и a<b вернет false

              >А чему равна 1/2?
              Очевидно 0.
              Ответить
              • > a проапгрейдится до unsigned и станет например 0хFFFFFFFB и a<b вернет false
                А теперь задумайся ненадолго - это здравый смысл, или знания, полученные за годы работы с сишкой? Через сколько месяцев или лет после первого знакомства с сишкой ты узнал это правило про операции с unsigned и signed одинакового размера?

                > Очевидно 0.
                Ну да, этот пример действительно слишком простой. Тут после пары тестов можно догадаться что происходит.
                Ответить
                • >Через сколько месяцев или лет после первого знакомства с сишкой ты узнал это правило про операции с unsigned и signed одинакового размера?

                  Когда достиг почтенного возраста. Уверен, что есть ещё очень много неизведанных граний. Нужно как-то взять стандарт и прочитать его наконец. Правда жить после этого станет не интересно, так как буду знать все тайны мироздания.
                  Ответить
            • > например если int a = -5 и unsigned b = 10, то что вернет a<b?

              Будет предупреждение и я сам приведу обе части к знаковому или беззнаковому типу так, как мне надо.
              Ответить
    • а ИДЕ это дает компилить?
      Ответить
      • правильная ИДЕ должна иметь возможность убивать быдлокодеров
        Ответить
      • а почему не должна? просто подчеркнет, что "мертвый код" и все.

        П. С. Не вижу ничего плохого в такой практике. Возможно забыли подчистить. Сам так иногда делаю.
        Ответить
    • вчера как раз пришлось писать заглушки после утилитного метода throwCustomException() который создавал нужный мэссыдж в одной централизованной функции без копипаста. глупый C# не понимал существование безусловного throw'а во вложенной функции и ругался, что после throwCustomException нет возврата значения, и ещё ругался насчёт отсутсивя break в switch.

      плохо сделано, я думал намного лучше будет.
      Ответить
      • > глупый C# не понимал существование безусловного throw'а во вложенной функции
        Если функцию поправят, и она внезапно перестанет вбрасывать исключение - весь код, который ее юзал получит утечку управления, что намного серьезнее описанной вами проблемы.

        Решением была бы, наверное, аннотация [NoReturn] или новый флажок в бинарном формате, выставляемый как в классе, где она описана, так и в классах, которые ее используют(!). И строгая проверка всего этого: если в классе А была функция, не возвращающая значения, а класс B ее юзал, то если класс B не пересобирали, а в классе А она иногда стала возвращать управление - должна быть выдана ошибка.

        И, видимо, никому не надо такой геморрой ради таких редких случаев...
        Ответить
        • >Если функцию поправят, и она внезапно перестанет вбрасывать исключение - весь код, который ее юзал получит утечку управления
          так .net всё равно верифицирует код на стадии jit-компиляции. если вдруг что-то поменяется (что вряд ли вероятно, т.к. такие функции обычно private/internal) то он просто бы выдал ошибку верификации
          Ответить
          • Дык а верификатор во многих случаях не обнаружит подобный баг:
            // было
            void throwCustomException() {
                throw new SomeException();
            }
            
            // стало (в результате неаккуратной правки Махараджниш Кумаром)
            void throwCustomException() {
                if (...)
                    throw new SomeException();
            }
            
            // использование
            void test() {
                bool someShit = ...;
                if (!someShit) {
                    throwCustomException();
                    // return писать влом,
                    // throwCustomException же всяко вбросит исключение
                }
                // после правки мы попадаем сюда даже если !someShit
                doSomething();
            }
            Этот код абсолютно корректен с точки зрения компилятора и верификатора, но doSomething() во втором случае получит управление, хотя писавший test() об этом не подозревал.

            При всем при этом компилятор не выдаст ни одного ворнинга. Поэтому, имхо, без аннотации [NoReturn] такие приемы с "гарантированным" вбросом исключения бесполезны и небезопасны.
            Ответить
            • В MS VS, если написать __declspec(noreturn), то он предложит вообще не ставить ретурн ниже по функции, например:

              void throwCustomException() {
                       throw new SomeException();
              }
              string test() 
              {
                      throwCustomException();
                      return "Батхерт"//Закомментировали эту строку и компилятор ругается варнингом, что не вернул значение из функции
              }
              
              /////////////////////////////////////////////////////////////////////////////////////////////////////////////////


              __declspec(noreturn) void throwCustomException() {
                       throw new SomeException();
              }
              string test() 
              {
                      throwCustomException();
                      // return "Батхерт"//Закомментировали и компилятор НЕ ругается варнингом, так как throwCustomException никогда не вернет управление.
              }
              Ответить
              • > он предложит вообще не ставить ретурн ниже по функции
                Ну все правильно делает. Здесь noreturn это часть контракта.

                P.S. В gcc тоже есть __attribute((noreturn)), но ворнинга про "недоступный код" на этом примере почему-то не выдается.
                Ответить
            • P.S. Лол, хайлайтер увидел throw в throwCustomException...
              Ответить
            • Смысл такой функции в избежании копипаста при создании объекта экспешона, если нужно сконструировать длинный Message с данными, например, и конструктор у экспешена имеет много параметров. В исходниках BCL такие утилитные методы для вброса исключений на каждом шагу. Так что там в идеале никаких if() не должно быть, потому что логики как таковой нет.

              Согласен, что это небезопасно. Но с учётом того, как много разного мусора затащено в .NET (минималистичным его не назовёшь), то появление поддержки такой фичи в рантайме не было бы уж таким "геморроем ради таких редких случаев" (повторяю, BCL кишит такими утилитными функциями).

              И, алсо, аннотацию лучше назвать не [NoReturn], а [MustThrow]. Как-то не ортогонально, что все методы могут быть и MustReturn, и нет (то бишь void), но MustThrow отсутствует, хотя это такой же exit point из функции.
              Ответить
      • а зачем вообще метод "void throwCustomException()"? чем не подошел "CustomException createNewCustomException();"? и, где надо, в явном виде выбрасывать исключение "throw createNewCustomException();"...и компилятору сразу все понятно, и от правок метода защищены
        Ответить

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