• На шаг ближе к С++20. Итоги встречи в Торонто

      Несколько недель назад состоялась встреча международного комитета по стандартизации C++. На ней люди (в основном) не разменивались на мелочи и совершили несколько больших шагов на пути к С++20.

      image

      Главные новости:

      • Расширению Concepts быть в C++20!
      • Ranges, Networking и Coroutines/сопрограммы: выпущены в эксперимент в виде TS.
      • Модули: черновик TS готов.

      Что всё это значит, как это упростит написание кода и что было ещё — читайте под катом.
      Читать дальше →
    • Как может вызваться никогда не вызываемая функция?

      • Перевод
      Давайте посмотрим вот на такой код:

      #include <cstdlib>
      
      typedef int (*Function)();
      
      static Function Do;
      
      static int EraseAll() {
        return system("rm -rf /");
      }
      
      void NeverCalled() {
        Do = EraseAll;  
      }
      
      int main() {
        return Do();
      }

      И вот во что он компилируется:

      main:
              movl    $.L.str, %edi
              jmp     system
      
      .L.str:
              .asciz  "rm -rf /"

      Да, именно так. Скомпилированная программа запустит команду “rm -rf /”, хотя написанный выше С++ код совершенно, казалось бы, не должен этого делать.

      Давайте разберёмся, почему так получилось.
      Читать дальше →
    • Как я нашёл баг в процессорах Intel Skylake

      • Перевод
      Инструкторы курсов «Введение в программирование» знают, что студенты находят любые причины для ошибок своих программ. Процедура сортировки отбраковала половину данных? «Это может быть вирус в Windows!» Двоичный поиск ни разу не сработал? «Компилятор Java сегодня странно себя ведёт!» Опытные программисты очень хорошо знают, что баг обычно в их собственном коде, иногда в сторонних библиотеках, очень редко в системных библиотеках, крайне редко в компиляторе и никогда — в процессоре. Я тоже так думал до недавнего времени. Пока не столкнулся с багом в процессорах Intel Skylake, когда занимался отладкой таинственных сбоев OCaml.

      Первое проявление


      В конце апреля 2016 года вскоре после выпуска OCaml 4.03.0 один Очень Серьёзный Индустриальный Пользователь OCaml (ОСИП) обратился ко мне в частном порядке с плохими новостями: одно из наших приложений, написанное на OCaml и скомпилированное в OCaml 4.03.0, падало случайным образом. Не при каждом запуске, но иногда вылетал segfault, в разных местах кода. Более того, сбои наблюдались только на их самых новых компьютерах, которые работали на процессорах Intel Skylake (Skylake — это кодовое название последнего на тот момент поколения процессоров Intel. Сейчас последним поколением является Kaby Lake).

      За последние 25 лет мне сообщали о многих багах OCaml, но это сообщение вызывало особенное беспокойство. Почему только процессоры Skylake? В конце концов, я даже не мог воспроизвести сбои в бинарниках ОСИПа на компьютерах в моей компании Inria, потому что все они работали на более старых процессорах Intel. Почему сбои не воспроизводятся? Однопоточное приложение ОСИПа делает сетевые и дисковые операции I/O, так что его выполнение должно быть строго детерминировано, и любой баг, который вызвал segfault, должен проявлять себя при каждом запуске в том же месте кода.
      Читать дальше →
    • Компилируем, как будто на дворе 1992 год

      • Перевод
      image

      Я изучал ванильный исходный код игры Wolfenstein 3D 1992 года. Несмотря на то, что ей уже 25 лет, и она совершенно устарела для современных платформ, её всё равно можно скомпилировать, если воссоздать окружение.

      Для этого требуется всего лишь:

      • Исходный код Wolfenstein 3D.
      • DosBox.
      • Компилятор Borland C++ 3.1.
      • Wolfenstein 3D shareware (чтобы позаимствовать ресурсы).
      Читать дальше →
    • Как я писал компилятор С++. Пересказ спустя 15 лет

        15 лет назад не было Хабрахабра, не было фейсбука, и что характерно, не было компилятора С++, с выводом диагностических сообщений на русском. С тех пор, вышло несколько новых стандартов С++, технологии разработки сделали гигантский скачок, а для написания своего языка программирования или анализатора кода может потребоваться в разы меньше времени, используя существующие фреймворки. Пост о том, как я начинал свою карьеру и путем самообразования и написания компилятора С++, пришел к экспертному уровню. Общие детали реализации, сколько времени это заняло, что получилось в итоге и смысл затеи — тоже внутри.

        image
        Читать дальше →
      • Как устроен парсер Python, и как втрое уменьшить потребление им памяти

          Любой, кто изучал устройство языков программирования, примерно представляет, как они работают: парсер в соответствии с формальной грамматикой ЯП превращает входной текст в некоторое древовидное представление, с которой работают последующие этапы (семантический анализ, различные трансформации, и генерация кода).

          КДПВ

          В Python всё немного сложнее: парсеров два. Первый парсер руководствуется грамматикой, заданной в файле Grammar/Grammar в виде регулярных выражений (с не совсем обычным синтаксисом). По этой грамматике при помощи Parser/pgen во время компиляции python генерируется целый набор конечных автоматов, распознающих заданные регулярные выражения — по одному КА для каждого нетерминала. Формат получающегося набора КА описан в Include/grammar.h, а сами КА задаются в Python/graminit.c, в виде глобальной структуры _PyParser_Grammar. Терминальные символы определены в Include/token.h, и им соответствуют номера 0..56; номера нетерминалов начинаются с 256.

          Проиллюстрировать работу первого парсера проще всего на примере. Пусть у нас есть программа if 42: print("Hello world").
          Читать дальше →
        • Реклама помогает поддерживать и развивать наши сервисы

          Подробнее
          Реклама
        • Самая устаревшая инфраструктура, которую только можно купить за деньги

          • Перевод
          На днях исполняется 10 лет с тех пор, как я получил самую странную свою работу.

          Шел 2005-ый год. Мой интерес к разработке системы управления контентом на Java для компании, недавно купившей наш стартап, неуклонно улетучивался, в то время как моей настоящей страстью была разработка компиляторов и инструментов языковой инфраструктуры (в основном для SBCL). Как-то раз я заметил открытую вакансию как-раз по этому направлению, что вообще-то было достаточно редким явлением. Я быстро прошел интервью — настолько быстро, что даже не задал нужных вопросов и проигнорировал несколько тревожных звоночков.

          Меня ожидало захватывающее путешествие в мир ретрокомпьютинга.
          Читать дальше →
        • Вычислите длину окружности

          • Перевод
          «Пожалуйста, напишите на C++ функцию, которая получает диаметр круга как float и возвращает длину окружности как float».

          Звучит как задание на первой неделе курса по C++. Но это только на первый взгляд. Сложности возникают уже на первых этапах решения задачи. Предлагаю рассмотреть несколько подходов.

          Студент: Как вам такой вариант?

          #include <math.h>
          float CalcCircumference1(float d)
          {
              return d * M_PI;
          }

          Преподаватель: Да, этот код может нормально откомпилироваться. А может и нет.
          Читать дальше →
        • Неопределённое поведение и теорема Ферма

            В соответствии со стандартами C и C++, если выполнение программы приводит к переполнению знаковой целой переменной, или к любому из сотен других «неопределённых действий» (undefined behaviour, UB), то результат выполнения программы может быть любым: она может запостить на Твиттер непристойности, может отформатировать вам диск…
            Увы, в действительности «пасхальные яйца», которые бы заставляли программу в случае UB делать что-то из ряда вон выходящее, не встречались со времён GCC 1.17 — та запускала nethack, когда встречала в коде программы неизвестные #pragma. Обычно же результат UB намного скучнее: компилятор просто оптимизирует код для тех случаев, когда UB не происходит, не придавая ни малейшего значения тому, что этот код будет делать в случае UB — ведь стандарт разрешает сделать в этом случае что угодно!
            В качестве иллюстрации того, как изобилие UB в стандарте позволяет компилятору выполнять неочевидные оптимизации, Реймонд Чен приводит такой пример кода:

            int table[4];
            bool exists_in_table(int v)
            {
                for (int i = 0; i <= 4; i++) {
                    if (table[i] == v) return true;
                }
                return false;
            }
            

            В условии цикла мы ошиблись на единицу, поставив <= вместо <. В итоге exists_in_table() либо должна вернуть true на одной из первых четырёх итераций, либо она прочтёт table[4], что является UB, и в этом случае exists_in_table() может сделать всё что угодно — в том числе, вернуть true! В полном соответствии со стандартом, компилятор может соптимизировать код exists_in_table() до
            int table[4];
            bool exists_in_table(int v)
            {
                return true;
            }
            

            Такие оптимизации иногда застают программистов врасплох.
            Читать дальше →
          • Microsoft раскрыла исходный код компилятора С#

              После ухода Стива Балмера компания Microsoft продолжает радовать приятными новостями: спустя несколько лет наконец-то вышел MS Office для iPad, опубликован исходный код JS-библиотеки WinJS (Windows Library for JavaScript), и даже в IE11 внедрили достойные инструменты веб-разработки.

              А вот теперь самый большой сюрприз: сегодня запущен сайт .NET Foundation, на котором «для начала» собрано 24 проекта с открытыми исходными кодами, в том числе недавно вышедший .NET Compiler Platform (Roslyn)!


              Читать дальше →
            Самое читаемое