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

image

С чего все начиналось


В далеком 2001-ом году, когда мне купили первый компьютер Duron 800mhz/128mb ram/40gb hdd, я стремительно взялся за изучение программирования. Хотя нет, сначала меня постоянно мучил вопрос, что же поставить Red Hat Linux, FreeBSD или Windows 98/Me? Ориентиром в этом бесконечном мире технологий для меня служил журнал Хакер.
Старый такой, стебный журнал. К слову, с тех пор, стиль изложения в этом издании почти не поменялся.

Виндузятники, ламеры, трояны, элита, линух — вот это все сносило крышу. Реально хотелось
поскорей освоить этот весь стек, которые они там печатали и хакнуть Пентагон (без интернета).
Внутренняя борьба за то, становиться ли Линуксоидом или рубится в игры на винде продолжалась до тех пор, пока в дом не провели интернет. Модемный, скрежечащий 56kb/s интернет, который занимал телефон на время подключения, и качал mp3-песню в районе получаса. При цене порядка 0.1$/mb, одна песня вытягивала на 40-50 центов. Это днем.

А вот ночью, были совсем другие расценки. Можно было с 23.00 до 6.00 залипать во все сайты не отключая изображения в браузере! Поэтому все что можно было скачать из сети за ночь, качалось на винт, и далее прочитывалось уже днем.
В первый день, когда мне домой провели и настроили сеть, админ передо мной открыл IE 5 и Яндекс. И быстро ретировался. Думая, что же первым делом искать в сети, я набрал что-то вроде «сайт для программистов». На что первой ссылкой в выдаче выпал совсем недавно открывшийся rsdn.ru. И на нем я стал зависать продолжительное время, испытывая чувство неудовлетворенности, от того, что мало что понимаю. На то время флагманом и самым популярным языком на форуме (да и вообще) был С++. Поэтому вызов был брошен, и ничего не оставалось, как догонять бородатых дядек в их знаниях по С++.
А еще был не менее интересный сайт на то время — firststeps.ru. Я до сих пор считаю их метод подачи материала наилучшим. Маленькими порциями (шагами), с небольшими конечными результатами. Тем не менее все получалось!

Активно скупая книги на барахолке, я стремился постичь все азы программирования. Одной из первых купленных книг было «Искусство программирования» — Д. Кнут. Не помню точную мотивацию купить именно эту книгу, а не какой-нибудь С++ для кофейников, наверное продавец порекомендовал, но я со всем своим усердием школьника взялся за изучение первого тома, с обязательным выполнением задач в конце каждой главы. Это была самая мякотка, и хотя с математикой у меня в школе не ладилось, но зато с мат.аном Кнута прогресс был, потому что было огромное желание и мотивация писать программы и делать это правильно. Осилив алгоритмы и структуры данных, я купил уже 3-ий том «Искусства программирования» Сортировка и поиск. Это была бомба. Пирамидальная сортировка, быстрая сортировка, бинарный поиск, деревья и списки, стеки и очереди. Все это я записывал на листочке, интерпретируя результат в своей голове. Читал дома, читал когда был на море, читал везде. Одна сплошная теория, без реализации. При этом я даже не догадывался, какую огромную пользу принесут эти базовые знания в будущем.
Сейчас, проводя собеседования с разработчиками, мне еще не встретился человек, который смог бы написать реализацию бинарного поиска или быстрой сортировки на листочке. Жаль.

Но вернемся к теме поста. Осилив Кнута, надо было двигаться дальше. Попутно я сходил на курсы Turbo Pascal, прочитал Кернигана и Ритчи, а за ними С++ за 21 день. Из С и С++, мне было не все понятно, я просто брал и переписывал тексты из книг. Загуглить или спросить было не у кого, но зато времени было вагон, так как школу я забросил и перешел в вечернюю, в которую можно было практически не ходить, или появляться на 3-4 урока в неделю.
В итоге с утра до ночи, я фанатично развивался, познавая все новые и новые темы. Мог написать калькулятор, мог написать простое приложение на WinApi. На Delphi 6 тоже получалось что-то нашлепать. В итоге, получив диплом о среднем образовании, я уже был подготовлен на уровне 3-4 курса университета, и разумеется на какую специальность идти учится вопроса не стояло.

Поступив на кафедру Компьютерных систем и сетей, я уже свободно писал на С и С++ задачи любого уровня сложности университета. Хотя, зайдя на тот же rsdn.ru, понимал, как много еще нужно изучить и насколько бывалые форумчане прокаченней меня в плюсах. Это задевало, непонимание и вместе с тем жгучее желание знать все, привело меня к книге «Компиляторы. Инструменты. Методы. Технологии» — А.Ахо, Рави Сети. В простонародье именуемой книгой Дракона. Вот тут и началось самое интересное. Перед этой книгой, был прочитан Герберт Шилдт, Теория и практика С++, в которой он раскрывал продвинутые темы разработки, такие как шифрование, сжатие данных, и самое интересное — написание собственного парсера.

Начав скрупулезно изучать книгу дракона, двигаясь от лексического анализа, затем к синтаксическому и наконец к проверке семантики и генерации кода, ко мне пришло судьбоносное решение — написать свой компилятор С++.
— А почему бы и нет, спросил себя?
— А давай, ответила та часть мозга, которая с возрастом становится все скептичней ко всему
новому. И разработка компилятора началась.

Подготовка


Модемный интернет к тому времени мне перекрыли, в силу смены телефонных линий на цифровые, поэтому для ориентира был скачан стандарт ISO C++ редакции 1998 года. Уже полюбившимся и привычным инструментом стала Visual C++ 6.0.
И по сути задача свелась к тому, чтобы реализовать то, что написано в стандарте С++. Подспорьем в разработке компилятора была книга дракона. А отправной точкой, был парсер-калькулятор из книги Шилдта. Все части пазла собрались воедино и разработка началась.

Препроцессор


nrcpp\KPP_1.1\
Во 2-ой главе в стандарте ISO C++ 98 идут требования к препроцессору и лексические конвенции (lexical conventions). Вот и славно, подумал я, ведь это наиболее простая часть и может реализоваться отдельно от самого компилятора. Другими словами, сначала запускается препроцессинг файла, на вход которому поступает С++ файл в том виде, котором вы привыкли его видеть. А после препроцессинга, на выходе мы имеем преобразованный С++ файл, но уже без комментариев, подставленными файлами из #include, подставленными макросами из #define, сохраненными #pragma и обработанной условной компиляцией #if/#ifdef/#endif.
До препроцессинга:
#define MAX(a, b) 	((a) > (b) ? a : b)
#define STR(s)  #s

/*
 This is the entry point of program
 */
int main()
{
	printf("%s: %d", STR(This is a string), MAX(4, 5));
}


После препроцессинга:
int main()
{
          printf("%s: %d", "This is a string", ((4) > (5) ? 4 : 5));
}


В довесок, препроцессор делал еще много полезной работы, вроде вычисления константных выражений, конкатенации строковых литералов, вывода #warning и #error. Ах да, вы когда нибудь видели в С-коде Диграфы и триграфы? Если нет, знайте — они существуют!
Пример триграфов и диграфов
int a<:10:>; // эквивалент int a[10];
if (x != 0) <% %> // эквивалент if (x != 0) { }

// Пример триграфа
??=define arraycheck(a,b) a??(b??) ??!??! b??(a??)
// проеобразуется в
#define arraycheck(a,b) a[b] || b[a]

Подробнее в вики.

Разумеется, основной пользой от препроцессора С++, является подстановка макросов и вставка файлов обозначенных в #include.
Чему я научился в процессе написания препроссора С++?
  • Как устроена лексика и синтаксис языка
  • Приоритеты операторов С++. И в целом как вычисляются выражения
  • Строки, символы, контанты, постфиксы констант
  • Структура кода

В целом, на написание препроцессора ушло порядка месяца. Не слишком сложно, но и нетривиальная задача, тем не менее.
В это время, мои одногруппники пытались написать первый «Hello, world!», да хотя бы собрать его. Далеко не у всех получалось. А меня ждали следующие разделы стандарта С++, с уже непосредственной реализацией компилятора языка.

Лексический анализатор


nrcpp/LexicalAnalyzer.cpp
Тут все просто, основную часть анализа лексики я уже написал в препроцессоре. Задача лексического анализатора — разобрать код на лексемы или токены, которые уже будет анализироваться синтаксическим анализатором.
Что было написано на этом этапе?
  • Конечный автомат для анализа целочисленных, вещественных и символьных констант. Думаете это просто? Впрочем просто, когда ты это прошел.
  • Конечный автомат для анализа строковых литеров
  • Разбор имен переменных и ключевых слов С++
  • Что-то еще, как пить дать. Вспомню допишу


Синтаксический анализатор


nrcpp/Parser.cpp
Задача синтаксического анализатора — проверить правильность расстановки лексем, который были получены на этапы лексического анализа.
За основу синтаксического анализатора были взяты опять же простенький парсер из Шилдта, прокаченный до уровня синтаксиса С++, с проверкой переполнения стека. Если мы например напишем:
(((((((((((((((((((((((((((((0))))))))))))))))))))))))))))))))); // кол-во скобок может быть больше

То мой рекурсивный анализатор съест стэк, и выдаст, что выражение слишком сложное.
У внимательного читателя, может возникнуть вопрос. А зачем изобретать велосипед, ведь был же yacc и lex. Да, был. Но на том этапе, хотелся велосипед с полным контролем над кодом. Разумеется в производительности он уступал сгенерированному этими утилитами коду. Но не в этом была цель — техническое совершенство. Цель была — понять все.

Семантика


nrcpp/Checker.cpp
nrcpp/Coordinator.cpp
nrcpp/Overload.cpp

Занимает соотвественно главы с 3-ей по 14-ую стандарта ISO C++ 98. Эта наиболее сложная часть, и я уверен, что >90% С++ разработчиков не знает всех правил описанных в этих разделах. Например:
Знали ли вы, что функцию можно объявлять дважды, таким образом:
void f(int x, int y = 7);
void f(int x = 5, int y);


Есть такие конструкции для указателей:
const volatile int *const volatile *const p;


А это указатель на функцию-член класса X:
void (X::*mf)(int &)


Это первое, что пришло в голову. Стоит ли говорить, что при тестировании кода из стандарта в Visual C++ 6, я не редко получал Internal Compiler Error.

Разработка анализатора семантики языка заняла у меня 1.5 года, или полтора курса универа. За это время меня чуть не выгнали, по другим предметам кроме программирования, за счастье получалась тройка (ну, ок четверка), а компилятор тем временем разрабатывался и обрастал функционалом.

Генератор кода


nrcpp/Translator.cpp
На этом этапе, когда энтузиазм немного начал угасать, уже имеем вполне рабочую версию фронт-енд компилятора. Что дальше делать с этим фронт-ендом, разработчик решает сам. Можно распространять его в таком виде, можно использовать для написания анализатора кода, можно использовать для создания своего конвертера вроде С++ -> C#, или C++ -> C. На этом этапе у нас есть провалидированное синтаксически и семантически AST (abstract syntax tree).
И на этом этапе разработчик компилятора понимает, что он постиг дзен, достиг просветления, может неглядя понять почему код работает именно таким образом. Для добивания своей цели, создания компилятора С++, я решил закончить на генерации С-кода, который затем можно было бы конвертировать в любой существующий ассемблерный язык или подавать на вход существующим Сишным компиляторам (как делал Страуструп в первых версиях «С с классами»).

Чего нет в nrcpp?


  • Шаблоны (templates). Шаблоны С++, эта такая хитровымудренная система с точки зрения реализации, что мне пришлось признать, без вмешательства в синтаксический анализатор и смешивания его с семантикой — шаблоны должным образом работать не будут.
  • namespace std. Стандартную библиотеку без шаблонов не напишешь. Да впрочем и заняло бы это еще много-много месяцев, так как занимает львиную долю стандарта.
  • Внутренние ошибки компилятора. Если вы будете играться с кодом, то сможете увидеть сообщения вроде:
    внутренняя ошибка компилятора: in.txt(20, 14): «theApp.IsDiagnostic()» --> (Translator.h, 484)
    Это либо не реализованный функционал, либо не учтенные семантические правила.


Зачем писать свой велосипед?


А в заключении хочу отметить то, ради чего писалась этот пост. Написание своего велосипеда, даже если на это потрачено 2 с лишним года, кормит меня до сих пор. Это бесценные знания, база, которая будет с Вами на протяжении всей карьеры разработчика. Будут меняться технологии, фреймворки, выходить новые языки — но фундамент в них будет заложен из прошлого. И на их понимание и освоение уйдет совсем немного времени.

github.com/nrcpp/nrcpp — исходники компилятора. Можно играться правя файл in.txt и смотреть вывод в out.txt.
github.com/nrcpp/nrcpp/tree/master/KPP_1.1 — исходники препроцессора. Собирается с помощью Visual C++ 6.
Поделиться с друзьями
-->

Комментарии (117)


  1. babylon
    27.02.2017 02:43

    Респект и уважуха! Взял бы уроки (изучил бы авторские статьи) по написанию интерпретаторов, компиляторов, VM и т.д.


    1. Jef239
      28.02.2017 18:59

      Увы. Автор прекратил работу там, где кончается общеизвестная теория и начинается know-how. То есть на кодогенерации под конкретный (или абстрактный, как в Java и С#) процессор.

      Как ни удивительно, но книг по кодогенерации очень мало. Одно из исключений — "Как паскаль и оберон попадает на Самсон". Как сделать архитектуру процессора — известно, а как сделать на неё кодогенерацию — know-how.

      Так что IMHO этот компилятор — хорошая дипломная работа. Но не более того.


      1. gigabite
        28.02.2017 21:24

        Я как раз изучаю С++, так что, ваша статья оказалась очень полезной (в плане мотивации), но насчет Оберона не понял. Есть смысл прочитать Вирта по операционным системам? Буду очень благодарен за ответ.


  1. xxvy
    27.02.2017 04:44
    +8

    Круто. Как я понимаю юнит-тесты в помощь не привлекали? По своему опыту скажу, что такие вещи (парсеры, лексеры и т.п.) очень удобно разрабатывать через TDD.


    1. Jef239
      28.02.2017 18:44
      -2

      Известный факт — компилятор алгола-68 написан на алголе-68. В момент, когда компилятор правильно компилирует сам себя — это означает проверку всего лишь 30% языковых конструкций.

      Тесты — полезная штука, но вычитка кода дает больше. То есть при вычитке кода можно найти те ошибки, на которые не хватило фантазии придумать тесты. Это я как человек. бывший 20 лет начальником группы тестирования говорю.


      1. 0xd34df00d
        28.02.2017 20:24
        +2

        Так никто не мешает после вычитки кода дописать набор тестов. А тесты сами по себе — это спецификация.


        Я как раз тоже пишу компилятор сейчас, пишу на хаскеле с тестами через hspec — одно удовольствие так разрабатывать, начиная от имеющегося инструментария вроде attoparsec для реализации парсеров и заканчивая сильной системой типов, а также сахарком вроде uniplate.


        1. Jef239
          28.02.2017 21:51
          -2

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

          Типичный пример, чем плохи собственноручные тесты: =+ в ранних версиях Си. В итоге пришло поменять на +=, ибо вводить обязательность пробела в конструкции i = +5 было ещё хуже.

          У каждого тестера тут своих историй навалом. Ну в общем держали 20 корректур


          1. 0xd34df00d
            01.03.2017 07:54
            +2

            Типичный пример, чем плохи собственноручные тесты: =+ в ранних версиях Си. В итоге пришло поменять на +=, ибо вводить обязательность пробела в конструкции i = +5 было ещё хуже.

            Не понял, причём тут тесты.


            1. Jef239
              01.03.2017 08:59
              +1

              Если тесты пишет не автор кода шансы обнаружить подобный ляп дизайна возрастают в разы.


        1. Sirikid
          01.03.2017 05:22

          А можно на него посмотреть? Пытался так писать, но не смог, видимо ещё недостаточно познал дзен TDD.


          1. 0xd34df00d
            01.03.2017 07:53

            К сожалению, нет, оно проприетарное и делаемое в рабочее время. Когда-нибудь, впрочем, я, возможно, напишу какую-нибудь статеечку о том, как подобные вещи можно делать.


      1. xxvy
        01.03.2017 04:11

        при вычитке кода можно найти те ошибки, на которые не хватило фантазии придумать тесты. Это я как человек. бывший 20 лет начальником группы тестирования говорю

        Всё правильно. Вы говорите как тестировщик. Я же имел ввиду разработку через TDD. Многие разработчики, ранее не практиковавшие TDD очень долго не могут понять его смысл.
        Так вот при разработке парсеров, лексеров, трансляторов через TDD этот смысл сразу приходит, т.к. именно здесь он прям и просится. И очень значительно облегчает сам процесс разработки.

        Ну а компиляция самого себя это как дополнительный бонус. Чертовски приятно что оно «живое» :). Как-то баловался в молодости написанием визуального конструктора простых интерфейсов. Сначала писал на языке разработки, но с какого-то момента понял, что дальше можно конструировать приложение, используя само приложение (попутно расширяя на низком уровне лишь полезными фичами).


        1. Jef239
          01.03.2017 04:20
          -2

          Разработка через тестирование дает менее соответствующее спецификациям приложение, чем независимое написание тестов и кода.


          1. xxvy
            01.03.2017 04:30

            Я и не спорю про спецификацию.
            Я про методологию разработки. Что именно в данном варианте она значительно облегчает сам процесс + как дополнительный бонус позволяет понять сам дзен этой методологии.
            По крайней мере со мной именно так :)
            И рекомендую остальным разработчикам, у кого есть желание «написать свой компилятор», и при этом не практикующие TDD (но имеющие желание начать).


            1. Jef239
              01.03.2017 05:07
              -2

              Какой именно процесс? Если процесс написания компилятора под свой собственный диалект языка — тогда согласен. Если под стандарт — не согласен. Обоснование читайте в книге Евгения Зуева "редкая профессия".


              1. xxvy
                01.03.2017 05:36

                Если под стандарт — не согласен.

                Не понимаю с чем вы не согласны. Я же не отрицаю ваши доводы по поводу полезности сторонних тестов.
                1. читаем книжку «редкая профессия»,
                2. берём «ещё два человека: тестировщик, не знающий устройства кода, и гуру, который судит, кто вернее прочел спецификацию — автор кода или автор тестов»,
                3. расчехляем (TDD) и, в процессе разработки, в ДОПОЛНЕНИЕ К ТЕСТАМ СПЕЦИФИКАЦИИ пишем свои модульные тесты, которые не обязательно тестируют уровень спецификации. Они могут тестировать более низкий уровень. Отдельные Функции, Методы, Модули.


                1. Jef239
                  01.03.2017 06:21
                  -3

                  И в момент, когда внутреннее тестирование закончено и надо сертифицировать продукт по внешний тестам — имеем скандал вплоть до мордобития и увольнения. Потому как по TDD — все работает. А по внешним тестам — не проходит.

                  И выясняется, что огромный массив тестов, написанный для TDD, годится только в мусорную корзину. :-)

                  TDD — хороший метод для написания собственного языка или собственного диалекта. Лет 25 назад я его использовал для электронных таблиц на УКНЦ. Но язык выражений там у меня был свой собственный.


                  1. xxvy
                    01.03.2017 06:41

                    TDD это не про тесты.
                    TDD это как их применять :).
                    Само по себе это не отменяет тестов спецификации.


                    1. Jef239
                      01.03.2017 07:06
                      -3

                      На самом деле это и то и то. TDD провоцирует стиль разработки «снизу вверх», что довольно вредно для больших программ. Ну в общем когда обожжетесь на TDD — тогда и поговорим.

                      Как говорит мой коллега «при разработке по TDD обычно работают только тесты.» :-) Имеется ввиду, что если человек решил, что в году 365 дней — то он это же проверит тестами. Это я про DOS-11. Через десяток лет 1 марта високосного года не попало на выходные и мы сильно удивлялись невозможности поставить 1 марта 1984 года. :-)


                      1. xxvy
                        01.03.2017 07:48

                        Всё правильно. Если он решил, что в году 365 дней, значит есть такой тест. При нахождении ошибки пишем новый тест, который падает и дорабатываем программу, чтоб тест проходил.

                        В любом случае, если он не написал тест на 365 дней, это не значит, что он не написал код, в котором год равен 365 дней :). Ошибка как по волшебству не исчезнет, но локализовать её будет сложнее.


                        1. Jef239
                          01.03.2017 08:58
                          -3

                          Не сложнее, а сильно проще. Подумайте внимательно, какого рода ошибки остабтся при TDD?


                          1. xxvy
                            01.03.2017 09:39
                            +3

                            Сильно проще разбираться в портянках лапшевидного кода, который писался на одном дыхании?
                            TDD провоцирует писать качественно структурированный код. Вот в чём всё дело.

                            Ещё раз повторю. Я ни в коем разе не уверяю, что на TDD свет клином сошёлся и что он панацея от всех бед. Я не призываю полагаться только на это.

                            Как и любой инструмент его можно применять неправильно (и напрасно ожидать от него чуда).
                            Точно так же глупо ждать чуда от статического анализатора, но он подстрахует от явных косяков непосредственно в момент творения косяка :).
                            Да можно писать в блокноте и не читать Warning-и компилятора. И оно даже будет соответствовать спецификации…


                            1. Jef239
                              01.03.2017 11:24
                              -3

                              TDD провоцирует писать качественно структурированный код.

                              Каким это образом?! Докажите, плиз.

                              Скорее уж наоборот, TDD (как и любые методы с ускоренным циклом разработки) приводят к плохо стуктурированной лапше. Ещё фича, ещё одна, быстрее, быстрее — а времени на то, чтобы проектировать структуру не остается.


                              1. xxvy
                                01.03.2017 12:05
                                +2

                                TDD (как и любые методы с ускоренным циклом разработки) приводят к плохо стуктурированной лапше. Ещё фича, ещё одна, быстрее, быстрее

                                Хм… До сих пор я был уверен, что мы с вами хотя бы об одном и том же говорим… Может напишете статью о вашем видении вреда TDD?
                                Поделитесь, своим 25+ опытом? Мне правда интересно.

                                А то здесь получается слишком длинно и беспредметно.
                                С вашего разрешения здесь я не буду продолжать :)


                                1. Jef239
                                  01.03.2017 15:52
                                  -3

                                  А выпячивание любого показателя на первое место — вредно.

                                  Платите за объем кода — получаете пухлый код в стиле Маяковского (ему тоже построчно платили). Платите за прохождение тестов — получаете код, который отлично работает на тестах, а на реальных данных — как получится.

                                  TDD хорошо сочетается с непрерывной интеграцией, всяким скрамом и так далее. То есть с работой, когда дальние цели (что надо через несколько лет) или не ясны или вообще лежат в плоскости «заработать N мешков денег». И как раз это и приводит к лапшевидному коду.

                                  А когда идет работа по плану, то какие-то этапы согласуются с идеологией TDD, а какие-то нет. Например, плохо согласуется с TDD разработка сверху вниз.

                                  Ну как пример. Делаем кубик рубика. Тестируем отдельные кубики, шарниры — вроде все хорошо. А при сборке — лажа.

                                  Опыт у меня специфический АСУТП — это ненастоящее программирование. Дай бог, цейтнот закончится — расскажу, как мы сделали систему размером 135кстрок при времени на отладку — 2 часа в месяц. В месяц, Карл! Два часа, Карл. Работает 365 на 24 уже почти 15 лет.

                                  Мы решили, что ошибки были есть и будут, поэтому система должна работать, несмотря на ошибки. И сделали это. Работает как Ванька-Встанька. Ушло на это примерно 30 процентов стоимости. Но ловля всех ошибок была бы дороже.

                                  Но при этом электронные таблицы 25 лет назад — делали в духе TDD. Потому что дял этого проекта, где язык дорабатывался в ходе написания парсера — TDD годится.


                                  1. Free_ze
                                    01.03.2017 17:54
                                    +2

                                    Тестируем отдельные кубики, шарниры — вроде все хорошо. А при сборке — лажа.

                                    Какое-то у вас странное TDD.


                                    1. Jef239
                                      01.03.2017 19:00
                                      -1

                                      обычное. Если у ошибка не случайна, она повторяется и в тестах и в коде. если мы решили, что все шарниры одинаковые — мы так и сделаем. И тесты пройдут. А на сборке выяснится, что половина должна быть зеркальная.

                                      Ну как пример — один из разработчиков (кажется Сергей Тарасов) с его-то решил, что число часов в сутках может меняться. Так оно не только в код, оно ещё и в доку попало. Десяток людей это читало и никто не заметил маразма, пока все это не прочел тестер (@yole).


                                      1. Free_ze
                                        01.03.2017 19:16
                                        +1

                                        И тесты пройдут.

                                        Интеграционные — не пройдут.

                                        Задача тестов в TDD — чтобы желаемое сошлось с действительным. Если у вас кривое «желаемое», то это не в инструменте дело, а в руках, которые его держат.


                                        1. Jef239
                                          01.03.2017 23:31
                                          -2

                                          Кривое желаемое — это обычная ошибка. Ничем не отличается от ошибки в коде, которые каждый из нас делает каждый день.

                                          Проблема TDD в том, что оно требует совмещать в одном человеке две разные роли. Не всякий хороший актер может быть хорошим критиком. И не каждый хороший критик может играть на сцене.

                                          Для каких-то областей, где ошибка желаемого редка — TDD пригоден. А там, где такая ошибка типична — не пригоден.


                                          1. Free_ze
                                            02.03.2017 11:18
                                            +1

                                            Кривое желаемое — это обычная ошибка.

                                            Это ошибка проектирования, архитектуры. TDD от этого не лечит и не должен.

                                            Могу понять ваше негодование, что этот подход не всесилен, но почему же он вреден?


                                            1. Jef239
                                              02.03.2017 15:55
                                              -1

                                              Мы о чем речь ведем? О написании компилятора стандартного С++. Стандарт С++ — огромен по сравнению со стандартом object pascal. Он не формализован, как стандарт алгола-68. Поэтому зачастую на двух программистов — 3 мнения, что имелось ввиду в стандарте.

                                              Таким образом, в конкретной задаче ошибка желаемого становится типовой. Отсюда основная претензия к TDD — этот метод потворствует ошибкам желаемого. Разумеется в сравнении с написанием тестов по методу черного ящика.

                                              Второй момент, очень похожий. Если я не вижу граничной ситуации при написании тестов — значит я не увижу её в коде. Таким образом TDD создает ложное впечатление о полноте тестирования. И опять-таки, спасает метод черного ящика.

                                              И опять-таки, в конкретной ситуации написания стандартного компилятора — это важно. А вот в написании сайта — не важно. Как и в написании компилятора для своего языка или своего диалекта языка.

                                              Дальнейшие претензии касается менеджмента. Как только мы вытащили TDD на первое место из большой обоймы методик экстремального программирования, сразу появляется опасность оценивать программистов по прохождению тестов. А это уже влечет за собой ситуацию «тесты работают, но работают только тесты», которую вживую наблюдал коллега. Вот это уже вредно.

                                              Как и ранее, спасает организация отдельной группы тестирования с конкурирующими интересами. Или парное программирование, когда один пишет тесты, а другой код.

                                              И последняя претензия — TDD не подходит для написания систем методом сверху вниз. Когда вначале придумывается архитектура, потом пишутся структуры данных и только на последних этапах появляется возможность использовать TDD.

                                              Иными словами, повернутость менеджмента на TDD приводит к написанию систем методом снизу вверх. Лет 50 назад вполне доказали, что для написания отдельных классов систем (в том числе компиляторов) этот метод хуже.

                                              С другой стороны, при развитии существующих систем этом метод вполне пригоден. Особенно он ценен для регрессионного тестирования.

                                              Мне вообще очень странно, что приходится разжевывать правила, которым меня учили в 17 лет. Правило простое — тестирование собственного кода — это профанация. Самообман. Втирание очков начальству. Да, метод «сначала тесты» несколько уменьшает отрицательные стороны самотестирования. Но далеко не до нуля.

                                              Таким образом приходим к выводам:

                                              • TDD полезен там, где он не заменяет группу тестирования. Ну или где группа тестирования отсутствует как класс.
                                              • TDD применим при итерационной разработке или при написании снизу верх
                                              • TDD не должен использоваться как основной инструмент оценки качества работы программиста


                                              Надеюсь, что моя позиция понятна.


                                              1. Free_ze
                                                02.03.2017 20:02
                                                +1

                                                Стандарт С++… Он не формализован

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

                                                Разработка снизу вверх такого сложного ПО — это верх идиотизма. Если этим болеет менеджмент, то какой спрос с инструмента разработки?

                                                TDD не запрещает разрабатывать сверху вниз.

                                                Парное программирование реализуемо и для TDD. Без разницы кто пишет тесты, лишь бы они были раньше, чем имплементация.

                                                TDD не отменяет QA. Равно как и юнит-тесты не отменяют его.


                                                1. Jef239
                                                  02.03.2017 21:47
                                                  -2

                                                  Жалко, что вы не читали формализованные стандарты языков.

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

                                                  Паскаль и алгол-60 использовали для описания синтаксиса БНФ, а алгол-68 — вВ-грамматику.

                                                  С формализаций семантики хуже, но тут, наверное, может просветить 0xd34df00d.

                                                  Увы, стандарт С++ не формализован ни на 0.001%.

                                                  Архитектор нашего проекта конкретной реализации подготовит свой документ, где все разночтения будут разрешены.

                                                  Не думаю, что вам удастся найти такого архитектора, который сможет назвать хотя бы 80% разночтений. Напоминаю историю про =+ в ранних версиях Си (а я на такой версии даже писал).

                                                  Без разницы кто пишет тесты, лишь бы они были раньше, чем имплементация.

                                                  Если программист имеет возможность прочитать тесты до реализации — это плохо (см. предыдущий пост), а если он видит тесты лишь после реализации — какая разница, когда они написаны?

                                                  TDD не отменяет QA.

                                                  Хорошо. Оставляем QA, отменяем TDD как критерий качества работы программиста. Разрешаем не писать ни единого теста на время разработки архитектуры и данных, то есть отходим от коротких циклов, принятых в TDD.

                                                  Какая тогда польза от TDD? Чем оправдываются его накладные расходы?


                                                  1. Free_ze
                                                    03.03.2017 10:25

                                                    Жалко, что вы не читали формализованные стандарты языков.

                                                    Не экспериментируйте с этим, говорят, затягивает.
                                                    Не думаю, что вам удастся найти такого архитектора, который сможет назвать хотя бы 80% разночтений.

                                                    Без комментариев.
                                                    Если программист имеет возможность прочитать тесты до реализации — это плохо

                                                    TDD-тесты должны отражать AC. Программист же может читать ТЗ до реализации, верно? =)
                                                    Какая тогда польза от TDD?

                                                    Очевидно, что это принудительная модуляризация, покрытие юнит-тестами стремится к 100%, что само по себе обеспечивает некоторый уровень качества (еще до QA и DevQA) и документирует код.
                                                    Чем оправдываются его накладные расходы?

                                                    Сокращением времени на дебаг и большим количеством тестов «из коробки».


                                                    1. Jef239
                                                      03.03.2017 12:48
                                                      -2

                                                      Программист же может читать ТЗ до реализации, верно? =)

                                                      ТЗ — можно читать, но не изменять. Знание тестов до реализации провоцирует удовлетворять требованиям тестов, а не ТЗ. В итоге получается, что написанием тестов мы изменяем ТЗ.

                                                      Хорошо это или плохо — зависит от качества ТЗ. Если ТЗ писали мы сами — нормально, а вот если ТЗ — это стандарт, то изменения его тихой сапой недопустимы.

                                                      принудительная модуляризация, покрытие юнит-тестами стремится к 100%, что само по себе обеспечивает некоторый уровень качества (еще до QA и DevQA) и документирует код.


                                                      Структуру модулей должен задавать архитектор системы. Как и межмодульные интерфейсы. Отдача её на самотек — допустима лишь в некоторых классах проектов, где не предусматривается время жизни (развития) системы в 20 и более лет.

                                                      Покрытие юнит-тестами — наоборот, полезнее всего для долго развивающихся систем. Оно обеспечивает регрессионное тестирование.

                                                      Документирование кода тестами вместо самодокументирующего кода — тоже имеет свою применимость. Если вместо одного темного места мы имеем два — понятнее не станет.

                                                      Сокращением времени на дебаг и большим количеством тестов «из коробки».

                                                      «Большое» количество не означает качества тестирования. Можно написать 100 тестов на положительных числах и забыть про отрицательные. :-)

                                                      Думаю, что и вам понятно, что тестирование и отладка в отладчике происходит раз в 5-10 быстрее, чем тестирование и отладка в пакетном режиме, то есть вставкой операторов печати и задания условий.

                                                      Так что или в сумме (написание тестов + сидение в отладчике) сокращения нет или оно достигается тем, что тесты покрывают далеко не все ситуации.

                                                      Остается один серьезный плюс — большое количество тестов для регрессионного тестирования. Если в системе частые рефакторинги реализации без рефакторинга архитектуры (частый случай) — это плюс. Если идет рефакторинг архитектуры — то модульные тесты все равно придется переписывать. Если рефакторинга вообще нет — это лишние трудозатраты.

                                                      А вот теперь о вреде. Подумайте, почему у вас программисты такого уровня, что вам приходится вводить меры для принудительной модульности? И насколько это связано с принудительным TDD?

                                                      Хорошие программисты — это коты, они работают там, где им нравится.И в той методологии, которая им нравится. Потому что куда уйти — у них всегда есть.

                                                      Главный вред — это когда методология, пригодная для частных случаев, возводится в абсолют. И начинает применяться за границами своей применимости.

                                                      Напоследок - цитата из Alex Young (кто был в FIDO, тот знает)
                                                      А не задумывались ли уважаемые джентльмены о том, что вот у католиков есть папа pимский, а у вегетаpианцев своего вожака нет, что у военных есть генеpалы, а холостяки каждый сам за себя? И все они не слишком-то стpемятся добиться обpатного. Или может pешим все за всех на пользу них же, глупых?


                                                      1. Free_ze
                                                        03.03.2017 14:08
                                                        +1

                                                        ТЗ — можно читать, но не изменять.

                                                        TDD не предполагает изменения. Более того, даже итеративные методики разработки, вроде Scrum, не позволяют менять ТЗ посреди спринта. А после каждого спринта должно быть «готовое к употреблению» ПО (или его функционирующая часть).

                                                        Знание тестов до реализации провоцирует удовлетворять требованиям тестов, а не ТЗ

                                                        Тесты пишутся по ТЗ, здесь не должно быть противоречий.

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

                                                        Согласен.

                                                        «Большое» количество не означает качества тестирования. Можно написать 100 тестов на положительных числах и забыть про отрицательные

                                                        Голову отключать — вообще порочная практика.

                                                        Думаю, что и вам понятно, что тестирование и отладка в отладчике происходит раз в 5-10 быстрее, чем тестирование и отладка в пакетном режиме, то есть вставкой операторов печати и задания условий.

                                                        Мне понятно, что тест найдет очевидную для него ошибку на порядки быстрее, чем отладчик подключится к процессу. Не говоря уже о процессе занимательного поиска места происшествия. Тесты же все равно придется писать, хоть перед кодом, хоть после… И, да, опережая ваши аргументы, логгирование и грамотную систему детектирования ошибок это не отменяет =)

                                                        почему у вас программисты такого уровня, что вам приходится вводить меры для принудительной модульности?

                                                        Бывают новички, бывает легаси. А юнит-тесты стимулируют рефакторинг, это тоже хорошо.

                                                        Хорошие программисты — это коты, они работают там, где им нравится.

                                                        Хорошие программисты дают хороший и поддерживаемый результат. Гениев-джедаев любят стартапы, а ненавистный вами интерпрайз или просто крупные продуктовые компании скорее сделают выбор в пользу нескольких «рабочих лошадок». Пусть это дороже, но bus factor никто не отменял.

                                                        Документирование кода тестами вместо самодокументирующего кода...

                                                        Возможно. Но что мешает писать тесты в поддержку самодокументирующемуся коду?

                                                        ЗЫ Цитата, кстати, интересная, но холиварить о ней я конечно же не буду =)


                                                        1. Jef239
                                                          04.03.2017 00:00
                                                          -1

                                                          Тесты пишутся по ТЗ, здесь не должно быть противоречий.

                                                          Пожалуйста, перечитайте весь тред. Мы уже говорили про разные интерпретации при чтении ТЗ и про ошибки при интерпретации. Не вижу смысла обсуждать по второму кругу.

                                                          (foo *)(*bar)(); — это что? Опережающее определение функции или вызов с приведением типа результата? Ответ по ссылкой на стандарт, пожалуйста.

                                                          TDD не предполагает изменения.

                                                          Тесты вносят свое понимание в ТЗ. И не факт, что это понимание будет верным. Умышленного изменения ТЗ тесты не вносят, но запросто добавляют вагон неумышленных изменений.

                                                          20 лет назад, будучи начальником группы тестирования и я такое сотворил. Могу рассказать, если интересно.

                                                          Сокращением времени на дебаг
                                                          Мне понятно, что тест найдет очевидную для него ошибку на порядки быстрее, чем отладчик подключится к процессу.

                                                          Вы про что, простите? Про тестирование или про отладку? Разницу между двумя этим процессами понимаете? А разницу между обнаружением ошибки по тесту и нахождением ошибочного места в коде?

                                                          Гениев-джедаев любят стартапы, а ненавистный вами интерпрайз или просто крупные продуктовые компании скорее сделают выбор в пользу нескольких «рабочих лошадок».

                                                          А если чуть грамотней — начальное написание крупного ПО и его сопровождение и развитие — это разные процессы. И как раз на стадии сопровождения и развития — TDD вполне годится, о чем я уже много раз писал.

                                                          А на стадии написания стандартного компилятора одним человеком — лучше ориентироваться на уже готовый набор тестов.

                                                          А обсуждали мы именно это — написание компилятора С++ одним человеком. См. тему статьи и начало треда.

                                                          И да, компиляторы в одиночку пишут в основном джедаи.


                                                      1. qw1
                                                        03.03.2017 22:45
                                                        -1

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


                                                        1. Jef239
                                                          03.03.2017 23:42
                                                          -1

                                                          Где бы мне найти таких кодировщиков, которые тупо кодили? Может и есть такие, да вот по квалификацию не подойдут. А те, кто по квалификации годится — тот не кодировщик, а программист. И разбирается в архитектуре не хуже меня.


                                                          1. qw1
                                                            04.03.2017 02:24

                                                            И всё же, разве программисты в вашем мире разработки похожи на независимых котов, которые делают то, что им нравится и так, как им нравится, если всё самое вкусное за них решил архитектор, а им остаётся лишь догрызть несущественные детали.


                                                            1. Jef239
                                                              04.03.2017 03:08
                                                              -1

                                                              Да даже не просто коты… ну не саблезубые тигры. но хотя бы оцелоты , сервалы, манулы

                                                              Ну как пример. Взяли специалиста по linux. А он решил, что ему интереснее делать платы. И теперь у нас свое железо. А потом решил, что linux — это лишние ОЗУ и ПЗУ. И итоге от операционки отказались. Ну почти отказались. FREERTOS — это 1% нормальной ОС. Зато — вес платы грамм 150. И она для беспилотников годится.

                                                              МЯУ!!!


                                                              1. qw1
                                                                04.03.2017 09:58

                                                                И как это соотносится с вашим

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


                                                                1. Jef239
                                                                  05.03.2017 16:25

                                                                  Мы о чем говорим? О том, как в тех командах, где я работал или о том, как писать компилятор стандартного С++?

                                                                  Там, где я работал и работаю- архитектором может быть каждый. Плюс-минус, но все достаточно грамотные и опытные. И главное — не придумать архитектуру, а убедить коллег в том, что эта архитектура лучше того, что придумали они. Ну или признать, что коллега придумал лучше (а это ещё сложнее).

                                                                  Если речь идет о компиляторе С++, то его надо разрабатывать по модели водопада. ну представьте себе, что вы сделали MVP, который из всех целочисленных типов умеет только int. На очередном спринте добавляете long, потом const, потом unsigned, потом volatile, потом short. Добавляете сразу на всех уровнях — лексический анализатор, синтаксический, кодогенерация. Будет у вас хорошая структура?

                                                                  Вот вам цитата от Евгения Зуева, чья команда писала этот самый компилятор С++
                                                                  У нас в свое время просто не хватило духу проговорить все до конца и определиться полностью по всем принципиальным вопросам. В результате некоторые существенные решения принимались "по умолчанию" тем или иным участником проекта без согласования с другими. Винить в этом, естественно, следует прежде всего старшего участника - автора этой статьи (как самого опытного, а не самого умного!).

                                                                  Так, компилятор сначала выполняет полный семантический анализ всего исходного текста, и только потом генерирует для всей программы результирующий код. Почему такая организация компилятора была выбрана третьим участником, до сих пор непонятно. Какое-то объяснение было тогда дано, но оно тут же выпало из нашей памяти, и вспомнить сейчас невозможно, а попытаться самим объяснить - не получается.

                                                                  Такое решение (удивительно, но принятое без всякого обсуждения) приводит к тому, что компилятор сохраняет полное дерево программы (и, следовательно, вынужден сохранять и семантические таблицы, так как они друг с другом сильно связаны) вплоть до завершения обработки всего исходного текста. Более логичным и экономичным был бы подход, согласно которому для каждой функции выполняется вся обработка, вплоть до генерации кода, после чего в структурах компилятора сохраняется только информация из ее заголовка, необходимая для компиляции вызовов. Исключение достаточно сделать для встраиваемых (inline) функций, да и то не всегда



                                                                  1. qw1
                                                                    05.03.2017 19:31
                                                                    +1

                                                                    Там, где я работал и работаю- архитектором может быть каждый.
                                                                    Это к замечанию «Структуру модулей должен задавать архитектор системы», которое якобы убережёт программиста от плохого дизайна. Это — не убережёт, если он сам себе архитектор (может, что-то другое убережёт).
                                                                    Если интересно — могу подробней рассказать о делении между архитектурой и программированием
                                                                    Если есть ссылки на материалы, было бы интересно. Сейчас мне не ясно, что мы получаем на выходе от «архитектора». Какой-то документ? Хотелось бы пример реального документа.

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


                                                                    1. Jef239
                                                                      06.03.2017 13:50
                                                                      +1

                                                                      То, что с архитектором может быть каждый, не означает, что на одном проекте несколько архитекторов. Это означает, что качество проекта архитектуры придется доказывать коллегам, которые понимают в архитектуре столько же или больше.

                                                                      Переделка архитектуры — это признак очень низкой квалификации. Или аналитика (не предусмотрели изменения ТЗ) или самого архитектора. Переделка архитектуры — это очень дорогостоящее решение. Обычно проще написать заново, чем кардинально переделывать архитектуру всей системы. Так что ваши системы или малы (до 10 тысяч строк) или ваша цель сделать шедевр, а не выпустить систему в установленный срок.

                                                                      Собственно что такое архитектура системы и архитектура ПО рассказано в вики.

                                                                      Определений много, я дам ещё одно. Представьте, что у вас 100500 программистов и вам нужно как можно быстрее создать систему. Какие части вы им выделите? Какие будут интерфейсы между этими частями? Вот сумма ТЗ для исполнителей — это и есть архитектура.

                                                                      Выделять много частей — нельзя, будут слишком много связей. Как писал Брукс "если 9 беременных женщин собрать вместе, ребенок через месяц не родится". Поэтому делим проект на подсистемы и описываем интерфейсы между подсистемами.

                                                                      Если брать компилятор С++, то я бы выделил такие поддсистемы:

                                                                      • Препроцессор
                                                                      • Таблица символов препроцессора
                                                                      • Подсистема ввода-вывода и кэширования
                                                                      • Лексический анализатор
                                                                      • Синтаксический анализатор
                                                                      • Таблица идентификаторов
                                                                      • Кодогенератор в p-code
                                                                      • Машино-независимый оптимизатор
                                                                      • Машино-зависимый оптимизатор
                                                                      • Вывод в .obj
                                                                      • runtime-библиотека компилятораj


                                                                      Ну это так, на первый взгляд. Ту же работу с темплейтами — возможно стоит выделить в отдельную подсистему.

                                                                      Какие-то документы по архитектуре может и можно показать, но только на мыло. Я всегда работал в небольших командах, а в команде до 10 человек нету особого смысла излишне все документировать.


                                                                      1. qw1
                                                                        06.03.2017 15:23

                                                                        Переделка архитектуры — это признак очень низкой квалификации
                                                                        Ага, лучше есть кактус самому и других заставлять, но не признаться, что сделал неправильно и оказался низкоквалифицированным.
                                                                        Переделка архитектуры — это очень дорогостоящее решение.
                                                                        Иногда да. Но часто бывают мелкие правки. Как то, объединить две подсистемы в одну (если они используют много общих данных) или наоборот, одну растолстевшую разбить на несколько.


                                                                        1. Jef239
                                                                          06.03.2017 17:07
                                                                          +1

                                                                          Переделка архитектуры — всегда дорогостоящее решение. В отличие от мелких изменений (тем более предусмотренных в ходе разработки архитектуры) или переделки того, что архитектурой не является.

                                                                          Разбиение подсистемы на две части с сохранением интерфейсов с остальными частями — это не переделка. Как и мелкое изменение интерфейсов. Переделка — это изменение принципов работы интерфейсов. ну например, у вас было понятие сессии и контекст влиента хранился на сервере. А тут вы решили перейти на stateless. Вот это — коренная переделка.

                                                                          Признать в ошибках — можно. В этом нет ничего страшного. Почитайте, какая горка костылей в unix. Но переделывать — себе дороже.

                                                                          Этим и отличается профессиональный подход от любительского. Любители делают шедевры, а профессионалу нужно хотя бы попытаться уложиться в бюджет.

                                                                          Как говорится — Ноев ковчег строили любители, профессионалы строили Титаник.


                                                  1. 0xd34df00d
                                                    04.03.2017 02:16

                                                    С формализаций семантики хуже

                                                    Ну, там всё плохо, да. Всякие традиционные сиплюсплюсы и прочие питоны описываются через некоторую пародию некоторое подобие операционной семантики, и то на неформальном языке (пусть и достаточно формально звучащем для человеческого уха). Доказывать что-то про свойства программ тяжело.


                                                    Для моего любимого хаскеля есть описание в денотационной семантике, где можно строить всякие теоремки, но там тоже есть свои тонкости (едва ли важные в рамках данной дискуссии, правда).


                                                    Более того, в случае C++ на ум сходу приходит такой вот курьёз: в нём не должно быть вечных циклов, которые ничего не делают, если упрощать, иначе это UB, а в семантически корректной программе UB быть не должно. Поэтому полноценный чекер, если бы он и существовал, должен был бы как минимум доказывать великую теорему Ферма (ну, три-четыре вложенных цикла, чего там) как минимум, как максимум — решать проблему останова.


                                                    1. Sirikid
                                                      04.03.2017 03:16

                                                      А как насчет Idris? В нем есть встроенный доказатель теорем и зависимые типы.


                                                      1. 0xd34df00d
                                                        04.03.2017 21:02

                                                        Я его пока близко не тыкал, ничего умного по существу сказать не могу.


                                              1. 0xd34df00d
                                                04.03.2017 02:10
                                                +4

                                                Таким образом TDD создает ложное впечатление о полноте тестирования.

                                                TDD никому никогда не обещает полноту тестирования.


                                                Когда вначале придумывается архитектура, потом пишутся структуры данных и только на последних этапах появляется возможность использовать TDD.

                                                Вполне подходит.


                                                Я писал синтаксический анализатор снизу вверх (потому что формальной спеки на язык нет, начальный набор тестов для TDD — примеры использования), а вот интерпретатор для отладки, кодогенератор и прочие подобные вещи — сверху вниз. И тоже с тестами и всем таким.


                                                Тут, кстати, для тестирования архитектуры помогает строгая типизация, когда можно сначала просто написать все типы данных и сигнатуры функций, поставив в определения undefined, а потом начать реализовывать сами функции, параллельно описывая для них тесты.


                                                1. Jef239
                                                  04.03.2017 02:47
                                                  -1

                                                  потому что формальной спеки на язык нет

                                                  А вот тут как раз TDD на своем месте. И уже на основании реализации и тестов — пишется стандарт. Как пример — правила расширенной области цикла в фортране, описывающие поведение первых компиляторов.

                                                  Все-таки дискуссия развернулась насчет использования TDD для написания компилятора стандартного С++. А это иная ситуация.


                                1. Jef239
                                  01.03.2017 15:54
                                  -2

                                  Все, вижу у вас в профиле энтерппрайс и веб-дизайн. Ну совсем иная область, в которой я не разбираюсь. Скорее всего там действительно вечные изменения ТЗ и TDD вполне в тему.

                                  Но мы тут говорили про компилятор стандартного С++. А не про сайты.


                              1. qw1
                                01.03.2017 18:03
                                +1

                                TDD провоцирует писать качественно структурированный код.
                                Каким это образом?! Докажите, плиз.
                                Некачественный код — это когда разработчик пишет большие блоки новой логики где проще, например прямо в функции main(). Или пишет большую колбасу кода прямо в событии OnClick какой-то кнопки.

                                Если есть требование, что код нужно тестировать, внутри main() как правило уже не попишешь, нужно выносить выносить логику в отдельные классы, чтобы тестовая среда создала эти классы и выполнила в них некоторые методы. Хочешь-не хочешь, придётся тестируемое поведение выносить в отдельный слой, отделять от графического интерфейса и скелета программы, вынося за абстракции.


                                1. Jef239
                                  01.03.2017 19:05
                                  -3

                                  А! Вы про школьников? Ну для школьников все верно, TDD отличный подход.

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

                                  я-то не про школьников говорил…


                                  1. qw1
                                    01.03.2017 21:12

                                    Может вы живёте в идеальном мире, где розовые пони какают бабочками, но мне приходится работать с совершенно разными людьми. Есть и 30-летние «школьники», сваливающие в main тонну кода, и убедить их непросто. Аргумент «потому что тестировать надо» вполне помогает.


                                    1. Jef239
                                      01.03.2017 23:12
                                      -2

                                      я живут в мире реальных вещей. Атомные ледоколы, локомотивы, беспилотники размером с домик, трактора, гендиректора, фирмы с двухбуквенными именами…

                                      Если вам нравится держать в команде тридцатилетнего школьника… ну значит вам так нравится.

                                      Для забивания гвоздей, микроскоп, конечно, подходит. А вот для того, чтобы смотреть на луну — совсем не годится. у TDD есть свои области применения, и не надо считать его панацеей.


                                      1. qw1
                                        02.03.2017 12:18

                                        Значит, везёт вам (пока что). У меня же выбор — или мириться, или учить, или увольняться.


                                        1. Jef239
                                          02.03.2017 15:58
                                          +1

                                          У меня выбора нет — я не умею работать там, где мне неинтересно. Потому уже почти 35 лет работаю в разных очень интересных мне командах.


                                          1. 0xd34df00d
                                            04.03.2017 02:20

                                            Интересно, что в нашей прошлой дискуссии о людях, которые уходят туда, где им интересно, вы отзывались скорее с негативными коннотациями. Либо я вас просто неправильно понял.


                                            1. Jef239
                                              04.03.2017 02:58
                                              -1

                                              Вы не путайте уход успешного программиста туда, где ему интересно и уход человека, который за год не довел ни одного проекта до работы с реальными данными. На тестах у него все летало, а как реальные данные — падало.

                                              Кстати, там, куда он ушел, ситуация повторилась. Теперь опять попросился к нам, но внештатником. И с договором, что основные деньги по результату на реальных данных.

                                              Может он и вырос, не знаю.

                                              Год назад у него весь пар в свисток уходил. То есть в написание структуры классов на все случаи жизни. Шик, блеск, гам — а включаешь, не работает.


  1. unixwz
    27.02.2017 07:12
    +2

    Отличная история, даже очень мотивирует делать свои велосипеды! Спасибо.


  1. vasiliysenin
    27.02.2017 09:14
    +1

    А что скажите на счёт rust, насколько он перспективен в качестве замены C++ в будущем?
    Если почитать Страуструпа, то он сам пишет что если бы не требование обратной совместимости, то он бы разработал С++ другим.


    1. agmt
      28.02.2017 00:15

      Жалко, что не получилось совместимости=(
      Да-да, я встречал живой C-код «int class;»
      А диграфы «or», «and» меня совсем огорчают.


      1. Orient
        28.02.2017 00:51
        +2

        Это не диграфы


  1. mwambanatanga
    27.02.2017 09:40

    Круто.

    Замечание: нигде в коде не указана лицензия (может, я просто не нашёл).


    1. stychos
      28.02.2017 16:45

      обычно такое под wtfpl подпадает


      1. mwambanatanga
        01.03.2017 09:31
        +1

        По обычным законам, всё с точностью до наоборот — если автор кода (и владелец всех прав на него) прямым текстом не дал другим людям какие-либо права на него (т.е. не приложил лицензию), то считается, что у других людей на этот код нет никаких прав (т.е. такой код считается закрытым).


  1. ConceptDesigner
    27.02.2017 11:45
    +1

    Да, трехтомник Кнута должен иметь в своем арсенале любой уважающий себя разработчик, не зависимо от языка программирования, который он использует. Соглашусь с автором, разработка своего велосипеда, хоть и не такого масштабного (например, написание своего std::map) повышает грамотность разработчика и способствует лучшему пониманию мироустройства.


  1. gatoazul
    27.02.2017 12:45
    +1

    Узнаю брата Колю. Я писал в молодости компилятор на Форте для языка собственного сочинения, сделав в начале что-то типа собственного yacc.


    1. Jef239
      28.02.2017 18:35
      -2

      Компилятор какого языка? Насколько я помню, компилятор форта написан на самом форте и занимал (как это и принято на форте) одну строку кода.


  1. ammaaim
    27.02.2017 13:31

    Круто! Помню в детстве программировал на тетрадном листочке на бейсике :D Прошли годы — сейчас допиливаю (just for fun) свой компилятор для C-подобного языка — заинтересовался этой темой лет пять назад с тех пор в свободное время занимаюсь этой безумно интересной темой. Куча времени потрачено, но опыт — бесценен :)


  1. Emelian
    27.02.2017 14:01
    -1

    > Это бесценные знания, база, которая будет с Вами на протяжении всей карьеры разработчика.

    Согласен, профессиональная база знаний у вас серьезная. Интересно, что вы думаете по поводу структуры индексных cdx-файлов для dbf-файлов базы данных Visual FoxPro? Это, судя по всему, B+-деревья с двойными списками на всех уровнях (судя по хилой картинке от M$). Смогли бы реализовать API для работы с ними? Похоже на то, что там реализованы, в т.ч., динамические массивы со свойствами связных списков. Что бы вы предложили для реализация этих самых «динамических массивов со свойствами связных списков»?

    Еще вопрос, который меня интересует чисто с теоретической точки зрения. Известно, что байт-код 1С8х относительно легко декомпилировать. А вы сможете, хотя бы для себя (это не надо публиковать) декомпилировать обфусцированный, скажем от Wise Advise или от Авы (Awa) байт-код? Здесь уже нужно изобретать нетривиальные алгоритмы по топологической сортировке циклических(!) плоских графов. Еще точнее, там нужны немножко другие малоизвестные алгоритмы, которые без хорошего знания математики вряд ли придут в голову.

    Никаких ответов, содержащих программный код, не требуется, только общие идеи.


    1. Stiver
      27.02.2017 22:44

      >> декомпилировать обфусцированный, скажем от Wise Advise или от Авы (Awa) байт-код? Здесь уже нужно изобретать нетривиальные алгоритмы [...]

      Все уже изобретено. Проблема декомпиляции байткода (в более общем виде: архитектур с разделением кода и данных) решена около 10 лет назад, алгоритмы свободно доступны.


    1. Emelian
      28.02.2017 07:12

      У меня тоже было написано выше: «Известно, что байт-код 1С8х относительно легко декомпилировать». Т.е., по смыслу, речь шла не о декомпиляции, а о деобфускации байт кода. Другими словами, если байт код 1С8х не обфусцирован, то легко декомпилируется (имеются обработки в открытом доступе от Awa), но если обфусцирован, то декомпилировать его весьма нетривиально. Тот же декомпилятор Авы обфусцирован, поэтому сам себя не компилирует, да и код 1С8х обфусцированный Wise Adbvise тоже не берет.


      1. Stiver
        28.02.2017 10:35

        Обфускация здесь не имеет значения, проблема решена в том числе и для irreducible CFG.


        1. Emelian
          28.02.2017 11:21

          > Обфускация здесь не имеет значения, проблема решена в том числе и для irreducible CFG.

          Ну, что ж, это ключевое замечание. Как там, у Мольера, Журден очень удивился, когда узнал, что всю жизнь говорил прозой. У меня этот «irreducible CFG» назывался «ориентированный циклический плоский граф (DCG), с минимальным количеством левосторонних связей». Соответственно свой алгоритм по его получению я назвал «Алгоритм минимизации левосторонних связей ориентированного плоского графа (циклического, в общем случае)». Может быть, вы подскажите, как называется соответствующий алгоритм в вашем случае?


          1. Stiver
            02.03.2017 11:34

            >> У меня этот «irreducible CFG» назывался «ориентированный циклический плоский граф (DCG), с минимальным количеством левосторонних связей».

            Хм… сомневаюсь, что мы говорим об одном и том же, но все может быть.

            Где-то году в 2008 я написал решение на примере Явы — декомпилятор Fernflower, теперь он часть IntelliJ IDEA:
            https://github.com/JetBrains/intellij-community/tree/5a805eea3e550c3605f2f01b6857858e170b7954/plugins/java-decompiler
            Оформить в статью руки так и не дошли, а поскольку код никто не читает :) то в 2015 его благополучно переоткрыли товарищи из Бонна:
            https://net.cs.uni-bonn.de/fileadmin/ag/martini/Staff/yakdan/dream_ndss2015.pdf


            1. Emelian
              02.03.2017 15:23

              > Где-то году в 2008 я написал решение на примере Явы — декомпилятор Fernflower

              Круто! Я знаком с вашим декомилятором, скачал где-то из просторов Интернет и даже немного пользовался им. Впечатления очень хорошие, правда, к самой Яве я быстро охладел, все-таки с++ с фреймворками мне импонирует немного больше.

              > Хм… сомневаюсь, что мы говорим об одном и том же, но все может быть.

              Речь идет об абстрактных структурах. Любой программный код с точки зрения связей это ориентированный граф, узлы которого (линейные операторы либо даже произвольные блоки кода) можно упорядочить на прямой (тот же ассемблерный код), со связями перехода. Если представить себе эти связи резиновыми, а узлы фиксированными (по принципу «чёрного ящика»), то, очевидно, код, без нарушения логики работы можно упорядочивать на прямой многими разными способами. При этом обфусцированный, а частности, сильно запутанный или перепутанный относительно связей код, будет содержать огромное количество как левых, так и правых связей (переходов). В том числе он может быть сильно перемешан с кодом, который никогда не получит управления. В таком случае мы имеем несвязный граф из которых нас интересует только та связная часть графа, которая получает управление.

              Исходя из сказанного, все же есть разница между декомпиляцией и деобфускацией. Например, байт код может быть не декомпилирован, но и не обфусцирован. А может быть не декомпилирован, но обфусцирован. Пока нас даже не интересует декомпиляция (скажем ассемблера в Си, как в IdaPro). Независимо от того, можем мы декомпилировать ассемблер в Си или не можем, важно сначала деобфусцировать запутанный код, а декомпилировать имеет смысл только регулярный, неулучшаемый уже в смысле распутывания либо максимального упорядочивания код.

              Так вот это неулучшаемое представление кода в виде максимально упорядоченного линейного связного графа с левыми и правыми связями и есть тот самый «irreducible CFG», судя по Википедии. Понятно, что при этом количество левых связей будет минимально возможным (при направлении потока управления слева направо). Например, бесконечный оператор, ссылающийся сам на себя, может быть представлен в виде двух линейных операторов, с одной левой и одной правой связью и одной входящей связью на любой из этих операторов. Очевидно, что это уже не улучшаемое представление, в данном случае, типичный пример «irreducible CFG».

              В случае обфусцированного кода, наша задача получить его сначала в виде «irreducible CFG». Как это сделать это уже другой вопрос. Еще раз подчеркну, пока о декомпиляции речь вообще не идет.

              Боннская статья, на которую вы ссылаетесь, похоже рассматривает только необфусцированный код, но нам его еще нужно получить из обфусцированного кода.

              У меня есть работающий алгоритм по получению неулучшаемого представления кода в виде упорядоченного связного линейного графа. В частности, байт-код 1С8х, обфусцированный Wise Advise он неплохо деобфусцировывает, а, следовательно, и позволят декомпилировать. С деобфускацией байт-кода от Awa сложностей немного больше. Но это не алгоритмические сложности, а технические, Уж очень изощренные методы применяет этот Ава. Но чисто декомпиляция Awa кода меня интересует абстрактно, просто как хорошее тестирование моего алгоритма. Главное, для меня найти математическое доказательство, что алгоритм работает всегда, а не только в данных частных случаях. Тогда можно будет и статью наваять, аналогичную как у «боннских товарищей» :).


              1. Emelian
                02.03.2017 15:31

                Кстати, если вы попытаетесь декомпилировать несвязный граф, то может выйти облом, поскольку байт код не получающий управление может быть, просто абсолютно бессмысленным, но при этом выполняющимся (дефектные ветви ведь не получают управления, а на уровне исполнения байт-кода его синтаксических проверок уже может не предусматриваться, как в том же 1С8х).


              1. Stiver
                04.03.2017 11:29

                >> Так вот это неулучшаемое представление кода в виде максимально упорядоченного линейного связного графа с левыми и правыми связями и есть тот самый «irreducible CFG»

                Нет, это совершенно другой термин. Irreducible (неприводимый?) граф — это такой граф выполнения, который не может быть выражен средствами структурного программирования, т.е. без использования goto. Посмотрите статьи на эту тему.

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

                Это не обфускация, а обыкновенное перемешивание блоков. Оно не имеет никакого значения, так как мы работаем с графом, а не с линейным порядком. Хотя примитивные декомпиляторы (типа jad'a) могут и споткнуться.


                1. Emelian
                  04.03.2017 15:17

                  >> Так вот это неулучшаемое представление кода в виде максимально упорядоченного линейного связного графа с левыми и правыми связями и есть тот самый «irreducible CFG»

                  > Нет, это совершенно другой термин. Irreducible (неприводимый?) граф — это такой граф выполнения, который не может быть выражен средствами структурного программирования, т.е. без использования goto. Посмотрите статьи на эту тему.

                  Не понимаю, где вы здесь увидели противоречие? Смотрим перевод «irreducible» в Multilex-2.0:

                  1. Не поддающийся упрощению, улучшению;
                  2. (мат.) Несократимый; неприводимый, несводимый;
                  3. Минимальный;
                  4. Непревратимый (в иное состояние);
                  5. (мед.) Невправимый;
                  6. (книжн.) Непреодолимый.

                  Это все варианты. В общем, то они говорят об одном и том же. Далее, смотрим определение cfg в википедии: https://en.wikipedia.org/wiki/Control_flow_graph

                  «A control flow graph (CFG) in computer science is a representation, using graph notation, of all paths that might be traversed through a program during its execution.» В переводе с буржуйского: «Граф потока управления (ГПУ) в информатике – это представление, с помощью графической нотации, всех путей, которые могут быть пройдены в рамках программы в ходе ее выполнения.»

                  Отсюда следует, что «Irreducible CFG» это неупрощаемый (неулучшаемый, неприводимый, несводимый, минимальный, канонический и т.п.) граф потока управления программного кода.

                  Кстати, причем здесь оператор GoTo? Направленный граф это просто кружочки со стрелочками. Ему может соответствовать код как с goto, так без него. Для графа это безразлично. В указанной вами статье «боннских товарищей» есть хороший пример cfg на рис. 3 (по сути это «Irreducible CFG», поскольку упростить этот граф уже нельзя). Смысл статьи в том, что авторы декомпилируют этот низкоуровневый «код» в псевдокод высокого уровня с помощью программы DREAM (рис. 5.1), а рядом (рис. 5.2) приводят псевдокод получаемый хорошо известным Hex-Rays от IdaPro Ильфака Гильфанова. Мол, вся фишка в том, что у Hex-Rays псевдокод использует оператор goto, а псевдокод от DREAM, goto не использует.

                  Но все дело в том, что я веду речь на уровне самого «графа потока управления». Да, когда этот cfg стал «иридьюсаблым», то тогда уже совершенно ультрафиолетово какой декомпилятор к нему применить. Если речь идет о Яве, то можно использовать ваш Fernflower, если о байт-коде 1С8х, то декомпилятор Авы, например, если об ассемблере, то Hex-Rays, либо DREAM (но когда он станет доступным, хотя бы в триале, это большой вопрос).

                  У меня задача другая, дан обфусцированный «неиридьюсаблый» граф потока управления. Мне нужно из него получить «иридьюсаблый» граф. Говоря по-русски, обфусцированный (перепутанный, намеренно усложненный) граф превратить в граф наиболее простого вида, далее уже неулучшаемого, несводимого, неприводимого и т.п. И только затем я буду применять к нему декомпилятор. В случае байткода 1С можно использовать даже собственный декомпилятор, построенный на базе идей Форт-машины.

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

                  > Это не обфускация, а обыкновенное перемешивание блоков. Оно не имеет никакого значения, так как мы работаем с графом, а не с линейным порядком. Хотя примитивные декомпиляторы (типа jad'a) могут и споткнуться.

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

                  Тот же байт-код 1С, даже необфусцированный, оператор гото (условный и безусловный) использует сплошь и рядом. Удобно, при собственной декомпиляции, на эту тему сильно не заморачиваться. Главное, чтобы декомпилированный код был работоспособным. Если цель состоит в том, чтобы подрегулировать что-либо только в одном месте, то полученного результата вполне достаточно. Если же нужно полное восстановление прикладного кода 1С, то просто внимательно читаем, осмысливаем, вычищаем и документируем полученный код. Эта задача относительно трудоемкая, но обозримая. За день можно таким образом обработать до 100 КБ кода высокого уровня.

                  Насчет линейного порядка. Узлы любого плоского графа (а может быть даже и многомерного, не проверял) можно уложить на прямую линию, полностью сохраняя топологию направленных связей. Относительно узлов любой граф линеен. Он нелинеен (плоский) относительно своих связей. Это достаточно очевидно для cfg (гпу). Тот же ассемблерный листинг бинарного кода, это линейно упорядоченный набор операторов. Другое дело, что множество переходов (связей) уже будет нелинейным. И то же самое для любого программного кода.

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


  1. brevis
    27.02.2017 14:46
    +4

    А я, кажется, как-то с тобой бухал :) (в славном городе на букву «Л»).
    Пожалуй это все, что я знаю про компиляторы :)


  1. KvanTTT
    27.02.2017 15:36

    А нельзя заменять все символы директив препроцессора на пробелы для сохранения корректных номеров строчек обычного C++ кода? Это может пригодиться для правильного репорта ошибок. Также смотри пример из моей статьи: препроцессор в Objective-C.


    1. qw1
      27.02.2017 20:41
      +3

      Это не поможет, потому что в си макрос может раскрыться в несколько строк, да ещё можно подключать код из других файлов через #include
      Например, в исходнике было 100 строк, а в выходе препроцессора — 250. Как ни пытайся, номера не сохранятся.

      Стандартное решение этой проблемы — директива #line

      Было на входе:

      #include "test.h"
      int x;


      стало на выходе
      #line 1 "test.h"
      int test_function1();
      int test_function2();
      #line 2 "main.cpp"
      int x;

      Компилятор смотрит на эти директивы #line и в отладочной информации связывает код с исходными строками и исходными файлами до препроцессинга.


      1. KvanTTT
        04.03.2017 15:57

        Хм, спасибо за разъяснение, не задумывался о таком. Однако же можно составить ассоциативную коллекцию "символ-позиция" до этапа препроцессинга, чтобы корректно потом сопоставлять позиции хотя бы для обычного кода без макросов? Хотя, возможно, такая сложность избыточна.


  1. Viacheslav01
    27.02.2017 15:44
    +1

    Для меня Кнут был недостижимой мечтой, отсутствовал в библиотеке, а один том в магазине стоил полторы зарплаты! Стоит ли говорить, о степени велосипедостроения в то время? Ну скажем так, я был удивлен, когда узнал, что бинароное дерево поиска это классика, а не мое личное изобретение :)


  1. BekzhanKassenov
    27.02.2017 21:54

    Крутая статья, читается на одном дыхании!

    Мне сразу же вспомнился другой рассказ о создании компилятора C++. До сих пор перечитываю эту статью, очень мотивирует.


    1. Kostia_1
      28.02.2017 23:11

      При много благодарен за рассказ. 32 листа поглотил на одном дыхании.


  1. Haibane_Tenshi
    27.02.2017 21:54
    +1

    Спасибо за мотивирующую историю. К сожалению, сейчас все чаще встречаю «программистов», знающих 1-2 фреймворка, но не имеющих базовых знаний. Теперь будет, что рекомендовать таким к прочтению.


  1. NickSin
    27.02.2017 21:54

    Браво!
    После вашего прочтения захотел перечитать читать книги, которые отложил на полку до лучших времен.


  1. Kostia_1
    27.02.2017 21:54

    Желание программировать у меня появилась сравнительно недавно. Как всегда стал вопрос, с чего начать. Первое, что посоветовали — С++. В 2007 году, мною была приобретена книга П. Франка «С++ учебный курс», издательства Питер, 2006 года. Но, книга была переводом, сделанным в 2004 году, а в самом материале велась полемика, что лучше Windows 3.1 или Windows 95. Я понял, что я влип. Но посчитав, что на само изучения основ языка это особо повлиять не должно, и как говориться, деньги потрачены, решил начать изучение. Большего издевательства над своим мозгом я не производил, с момента использования справочников, по проектированию тяговых (силовых) подстанций, при написании дипломной работы. В общем все усилия сошли на нет благодаря нудности изложения, лени (хотя ее скорее надо было поставить в начало списка) и нежелании трать время на данный. как мне казалось, треш.Потом в нашу жизнь ворвался андроид. Желание попытать счастья в кодинге проснулось с новой силой. В виду отсутствия по ряду причин, возможности приобретения книги, была сделана распечатка ее копии и прошивка в два тома. Сам процесс был не простым но затягивающим. В книге оговаривалась, при чем безоговорочно, среда разработки Eclipse. Установил я сие чудо, начинаю копать глубже по страницам книги, и натыкаюсь на искрометную фразу: «Для нормального программирования в Eclipse вам необходимо хорошие знания языка программирования Java...» И это в книге о программировании приложений для андроид с нуля. В общем опять шляпа. Немного отлегло от сердца, думаю вернусь к начатому с андроидом. Каково мое было удивление, когда я узнаю, что Eclipse не поддерживает разработку выше 5-й версии андроида и то при условии установки новой версии Mars. С Майкрософтофским софтом таже печалька. Их визуальный экспресс уже поменял бесчисленное количество переизданий, как минимум одно переиздание в два года. «Hello World» в них занимает хороших пол листа кода-мусора, который компилятор впихивает его туда, как принято у мягкотелых, на всякий случай. К чему все это я, а стоит принять еще одну попытку, и с чего стоит начать...?


    1. Sirikid
      27.02.2017 23:35

      Потом в нашу жизнь ворвался андроид… В книге оговаривалась, при чем безоговорочно, среда разработки Eclipse… «Для нормального программирования в Eclipse вам необходимо хорошие знания языка программирования Java...» И это в книге о программировании приложений для андроид с нуля.

      Лул, это случайно не "Android. Разработка приложений для чайников"? У меня начиналось все точно также, правда под Android я так до сих пор ничего и не написал...


      1. Kostia_1
        28.02.2017 00:16

        Она самая.


    1. trix
      28.02.2017 11:19

      Если вы сделали выводы из статьи выше, то вам надо изучать «базу», т.е. ту самую яву. Eclipse там или нет — никакой роли для начала не играет. Литература и куча всяких курсов на любом языке (хотя я бы сильно рекомендовал английский) легко гуглится. Через месяца три занятий по базовой яве можно пробовать андроид.


      1. Kostia_1
        28.02.2017 23:06

        Я, на подсознательном уровне, если так можно выразиться, тяготею к С++. К Java, я пока не дорос. Объясню почему. У меня в голове есть идеи создания небольших утилит, которые в теории, помогут очень облегчить мою рутинную жизнь на работе. При чем, это такие аспекты, где виртуальные машины и прочие прелести жабы не уместны. Рискнул опять начать все сначала. Восстановил учетку на мелкософтовском комьюнити, заново переустановил экспрес визуалку и радую по вечерам себя лицезрением уроков Маркова по С++. Как то так. :)


        1. trix
          28.02.2017 23:10
          +1

          для небольших утилит уместнее что-то типа питона использовать, а есть у явы ВМ или нет — не столь важно, главное, что новичку там сложнее прострелить себе ногу :) плюс много готовых библиотек на все случаи жизни и хорошая помощь от IDE


          1. Kostia_1
            28.02.2017 23:24

            Пример из реальной ситуации. На win-машине нужно было менять сетевые настройки, выкрутился скриптами и батниками (netsh). Для удаленного управления настройками shdsl Zyxel Prestige по телнету(минимум загрузки канала) никакие скрипты и прочие костыли не помогали. Начал копать глубже и нашел решение — TSE (Telnet Script Executor) Любимкова Дмитрия. Чистокровная С++ программа, которая намного облегчила мне жизнь. И вот с того времени меня не покидает идея двигаться в этом направлении(кодинга) дальше по принципу от малого к великому. Может мне просто не попадались в живую хорошие легковесные решения на джаве, которыми я бы смог воспользоваться на практике. Просто из того что мне приходилось видеть вызывало только отвращение не только к жабе, но и к кодингу в принципе. :)


            1. Wedmer
              01.03.2017 00:16

              Если сидите чисто на форточках, то почему бы вам не попробовать C#. [dot]Net из коробки умеет достаточно много. Для быстрых набросков самое оно.
              Если уж хочется именно C++, то можно попробовать С++/CLI (с моей точки зрения это изврат).
              А еще лучше (IMHO) начать со связки Qt + QtCreator.

              Но самое важное понимать, как достичь цели при помощи правильно подобранного инструмента.


              1. Kostia_1
                01.03.2017 00:47

                Форточки — 90% моего соприкосновения с ПК и 10% — это mikrotik.Все таки, пока хочу базу начать с визуального, а там, если «полетит — не полетит» то можно будет найти какой то фреймворк под себя. Связку Qt + QtCreator взял на заметку.


    1. Error1024
      02.03.2017 07:40
      -1

      Пробуйте Delphi, как бы не хаяли, но это одна из немногих сред, не обросших говном в виде длинных мастеров создания проекта, кучи сгенерированного имиже кода и т.д.
      Запустил, нажал пару кнопок, и можешь творить :)


      1. Kostia_1
        02.03.2017 13:23

        Вы не поверите, но к Delphi я пытался приобщится с 2005 года. Приобрел «Delphi 7 учебный курс» С. Бобровского, издательства Питер и в 2006 «Delphi базы данных и приложения» С.П. Кандзюба+ В.Н. Громов, издательства Диасофт. Сначала, нашел ломаную дельфийскую 7-ку, сделал даже слепок. Запустил, чего то там даже начал «Приветы миру» передавать. Потом смена профессии и приоритетов. Ушло все на второй план. Через время «вернувшись в тему», увидел, что дружба Delphi с майкрософтом осталась на уровне ХР. Тема осталась без продвижения, из-за очевидности тупика. Выход борландовских Х-ов уже ничего кардинально не менял. Может я и не прав, в своих суждениях. =)


        1. Error1024
          02.03.2017 14:54
          -1

          Современные версии отлично дружат с Windows 10, и имеют поддержку многих новомодных «фишек».
          В целом вам необходимо научиться программировать, и не важно есть что-то модное или нет в Delphi, главное тут то, что порог вхождения в разы ниже.


          1. Kostia_1
            02.03.2017 23:38

            Волей случая, пришлось кое что делать из тестовых заданий, для дочери знакомых, в Lazarus. Не скажу, что меня все устроило, но до воли сносно. А главное, что софт условно бесплатный, ничего крякать/хакать/мутить не нужно. Я так думаю, что мои книги, по крайней мере, в теории должны подойти.


  1. id01
    27.02.2017 21:55

    В программировании я недавно. Почувствовал, как мои «знания» утонули в океане уважаемого автора.
    Респект.


  1. sv11
    27.02.2017 21:55
    +1

    Автор — фанатик в хорошем смысле этого слова. Мне вот тоже высшая математика плохо давалась. Пока на пришлось писать программу для расчета ректификационной колонны в маткаде (там была возможность делать встроенные программки). Потом уже дело и до С++ дошло…
    З.Ы. Мне одному кажется, что код на титульной иллюстрации не скомпилируется?


    1. bevice
      28.02.2017 16:07

      Вас лишняя «n» смутила?


      1. sv11
        28.02.2017 16:29

        ага


  1. billyevans
    28.02.2017 04:48
    +2

    А главный вопрос готовности компилятора, может ли он сам себя собрать?


  1. gorodnev
    28.02.2017 09:36
    -1

    Сейчас, проводя собеседования с разработчиками, мне еще не встретился человек, который смог бы написать реализацию бинарного поиска или быстрой сортировки на листочке. Жаль.


    Даже странно, я таких немало повидал. Тем более, что вопросы довольно штатные для компаний вроде Yandex/Google/Facebook.


  1. saipr
    28.02.2017 12:20

    Написание своего велосипеда, даже если на это потрачено 2 с лишним года, кормит меня до сих пор. Это бесценные знания, база, которая будет с Вами на протяжении всей карьеры разработчика. Будут меняться технологии, фреймворки, выходить новые языки — но фундамент в них будет заложен из прошлого. И на их понимание и освоение уйдет совсем немного времени.


    О том как писались ОС 30 лет назад:

    30-летие учебного пособия ОС Minix


  1. alz72
    28.02.2017 12:57
    +1

    Круто — что еще сказать, может на пенсию выйду и попробую повторить сей подвиг ;), автору репспект !


  1. Jef239
    28.02.2017 19:05
    -2

    А чем ваш компилятор лучше остальных? Какие преимущества у него есть?


  1. Door
    01.03.2017 23:41

    Круто. Спасибо за статью.
    Для тех, кто хочет посмотреть/подебажить всё вместе — держите CMakeLists.txt:


    CMakeLists.txt
    cmake_minimum_required(VERSION 3.4)
    
    project(nrcpp)
    
    # Remove unneeded configurations
    set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "" FORCE)
    
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4267 /wd4244 /wd4996 /wd4018 /wd4477")
    
    file(GLOB kpp_src "KPP_1.1/*.cpp")
    file(GLOB kpp_hds "KPP_1.1/*.h")
    set(kpp_other_files 
        KPP_1.1/in.txt
        KPP_1.1/in.txt.comment
        KPP_1.1/in.txt.slash
        KPP_1.1/in.txt.trig
        KPP_1.1/limits.h.comment
        KPP_1.1/limits.h.slash
        KPP_1.1/limits.h.trig
        KPP_1.1/out.txt)
    
    add_library(nrcpp_lib nrcpp/LIB/NRC.H nrcpp/LIB/NRC.CPP)
    target_include_directories(nrcpp_lib PUBLIC nrcpp/LIB)
    
    add_executable(KPP ${kpp_src} ${kpp_hds} ${kpp_other_files})
    
    file(GLOB nrcpp_src "nrcpp/*.cpp")
    file(GLOB nrcpp_hds "nrcpp/*.h")
    set(nrcpp_other_files 
        nrcpp/in.txt
        nrcpp/out.txt)
    
    add_executable(nrcpp ${nrcpp_src} ${nrcpp_hds} ${nrcpp_other_files})
    target_link_libraries(nrcpp nrcpp_lib)


  1. apborezkiy
    04.03.2017 10:18
    +1

    А я писал только компилятор слегка кострированного С, но действительно, фундамент заложился очень сильный :) Написать же компилятор С++, это просто респект!


  1. Costic
    05.03.2017 09:30
    +1

    Интересная статья, спасибо за возможность посмотреть исходники. Жалко, что много комментариев не читаемы в Гите.
    Местами в вашей статье вижу снобизм, но вы в своём коде смешиваете string-классы и «строки». Я удивлён, что не встретил хэш-функций в таком проекте. Ваша реализация isdigit8 и ей подобных неэффективна, использование register тоже удивило Хотя это мелочи, так же как и BSP. :-)
    В 2000 году я закончил свой транслятор, защитился и убрал свой «велосипед» в долгий ящик. У меня не было полноценного генератора ассемблерного/машинного кода. Я думал, что увижу у вас… Но не нашёл.
    Меня в 1993 году вдохновила книга Дьюхарст С., Старк К. «Программирование на С++». В формате ДежаВю http://www.nehudlit.ru/books/detail1193725.html можно полистать. На 139 и 141 страницах идеальная (на мой взгляд) реализация для парсера в виде дерева, к которой можно прикрутить генератор кода.


  1. babylon
    06.03.2017 06:08

    За 15 лет можно уже написать ещё один или три велосипеда на на свой велосипед...