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

    +2

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    12. 12
    13. 13
    14. 14
    15. 15
    16. 16
    17. 17
    18. 18
    19. 19
    20. 20
    21. 21
    22. 22
    23. 23
    24. 24
    25. 25
    26. 26
    27. 27
    28. 28
    29. 29
    30. 30
    31. 31
    32. 32
    33. 33
    34. 34
    35. 35
    36. 36
    37. 37
    38. 38
    39. 39
    40. 40
    41. 41
    42. 42
    43. 43
    44. 44
    45. 45
    46. 46
    47. 47
    48. 48
    49. 49
    50. 50
    51. 51
    52. 52
    53. 53
    54. 54
    55. 55
    56. 56
    57. 57
    58. 58
    59. 59
    60. 60
    61. 61
    62. 62
    63. 63
    64. 64
    65. 65
    66. 66
    67. 67
    68. 68
    69. 69
    70. 70
    71. 71
    72. 72
    73. 73
    https://habr.com/ru/article/448466/
    
    */
    А последние пару лет набирает популярность boost.hana. Это boost.fusion,
    но с constexpr'ом и лямбдами. Автор hana, Луис Дионе (Louis Dionne),
    используя на полную мощь все возможности новых стандартов, в некотором
    смысле очеловечил метапрограммирование. С помощью hana всяческие
    манипуляции с типами, их кортежами, отображениями, а также компайл- и
    рантайм-работа с ними обрели практически человеческое лицо и читаемый
    вид. Ну, с поправкой на синтаксис C++, разумеется. Вот, например, как выглядит
    универсальный сериализатор структур, написанный с помощью hana:
    */
    
    // 1. Give introspection capabilities to 'Person'
    struct Person {
      BOOST_HANA_DEFINE_STRUCT(Person,
        (std::string, name),
        (int, age)
      );
    };
    // 2. Write a generic serializer (bear with std::ostream for the example)
    auto serialize = [](std::ostream& os, auto const& object) {
      hana::for_each(hana::members(object), [&](auto member) {
        os << member << std::endl;
      });
    };
    // 3. Use it
    Person john{"John", 30};
    serialize(std::cout, john);
    
    /*
    Ну да, структуры приходится описывать особым образом. А кому сейчас легко?
    И тут хочется также отметить библиотеку tinyrefl за авторством Manu Sánchez
    (Мануэля Санчеса). Довольно неплохая попытка принести в мир C++-разработки
    статическую рефлексию без расширения компилятора. Для своей работы библиотека
    требует стороннюю утилиту (cppast), но зато предоставляет довольно удобный доступ
    к информации о структурах, которую можно использовать в процессе разработки
    программы. Преимущество этой библиотеки (по сравнению с другими) в том, что для
    работы с ней не надо использовать «птичий язык», а элементы структур (как и сами
    структуры) можно произвольным образом атрибутировать прямо в исходном коде:
    */
    
    struct [[serializable]] Person {
        std::string name;
        int age;
    };
    template<typename Class>
    auto serialize(std::ostream& os, Class&& object) -> std::enable_if_t<
            tinyrefl::has_metadata<std::decay_t<Class>>() &&
            tinyrefl::has_attribute<std::decay_t<Class>>("interesting"),
        std::ostream&>
    {
        tinyrefl::visit_member_variables(object, [&os](const auto& /* name */, const auto& var) {
            os << var << std::endl;
        });
        return equal;
    }
    
    /*
    Таких примеров можно привести ещё много (или найти на гитхабе или гитлабе).
    Объединяет все эти библиотеки и инструменты одна особенность: значительно
    облегчая жизнь своим пользователям, они имеют такую реализацию, что остаётся
    лишь предполагать, какое количество страданий испытали их разработчики. Достаточно
    заглянуть в реализацию, увидеть забор из ifdef'ов и угловых скобок — и всё понятно.
    Нередко эти реализации делаются на грани возможностей компиляторов. workaround'ы
    банальных ошибок (или особенностей реализации языка конкретной версии конкретного
    компилятора с конкретными флагами) здорово раздувают их код. Желание поддерживать
    несколько стандартов сразу заставляет придумывать зубодробильные конструкции.
    Безусловно, простая попытка реализовать что-нибудь подобное здорово поднимает уровень
    владения языком (иногда — и самооценку). Но в какой-то момент, после многочасовой возни
    с очередной ошибкой, или непроходящим тестом, или крэшем компилятора руки опускаются,
    хочется плюнуть и бросить, ибо силы бороться иссякают.
    */

    Именно поэтому я за гомоиконность

    Запостил: j123123, 21 Апреля 2019

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

    • > 1.4 Генерация кода

      > Но это, как говаривал Барон, ещё не все. Однажды в мир разработки C++ пришёл clang со своим фронтендом (а чуть позже — с libtooling), и мир изменился, заиграл новыми красками. Появилась надежда. Ибо появился инструмент, позволяющий относительно просто (относительно!) анализировать исходные тексты на C++ и что-нибудь с результатами этого анализа делать. Появились (и стали набирать популярность) утилиты типа clang tidy, clang format, clang static analyser и прочие. А некоторые разработчики приспособили clang frontend для решения задач рефлексии. Так, скажем, работают упомянутые выше cppast (за авторством Jonathan Müller) и tinyrefl на её базе. Так работает автопрограммист, используемый в нашей компании для кодогенерации (или его open-source-аналог).

      > Разумеется, и до появления clang писались генераторы на основе исходного кода C++. Кто-то использовал для этого Python с самописными парсерами, кто-то разбирал C++ с помощью regexp'ов. Кто-то, как автор этой статьи, писал самопальный парсер на C++. Но, полагаю, с развитием инструментария это будет уходить в прошлое.

      > Идея проста. Берётся исходный текст на C++ (например, объявления структур), пропускается через clang frontend, и на основе полученного AST генерируется новый исходный текст.

      Именно поэтому.
      Ответить
    • > Это может выглядеть забавно, но сейчас уже заходит речь об интеграции в компилятор оптимизирующих JIT-движков. Потому как constexpr'ы как-то медленно вычисляются...

      Я уже не один раз высказывал эту гениальную мысль на страницах говнокода

      http://govnokod.ru/25397#comment460314
      http://govnokod.ru/25407#comment461860
      Ответить
      • https://twitter.com/hankadusikova/status/1115351646178029568
        > Currently the performance is really bad, there is a talk to make wasm based jit machine in a compiler to accelerate constexpr, but problem is keeping the undefined behavior-less behavior :)

        бггг
        Ответить
        • https://twitter.com/hankadusikova/status/1118851506210521088

          > We really need an allocation mechanism in constexpr. But I don't need to return the allocated object outside of a constexpr function...

          > Is std::array constexpr? If so, can it decay to a ptr and be cast to any type at will?

          > You can’t cast to any type in constexpr.

          https://memepedia.ru/wp-content/uploads/2017/07/хохочущий-испанец.jpg
          Ответить
        • https://twitter.com/hankadusikova/status/1115319734386532353
          > I really need C++ to be able generate goto-s and labels.

          ох блядь... make me unsee it
          Ответить
    • > 3.1 Основы концепции
      > Макросы (C++), шаблоны (C++98), свойства типов и constexpr'ы (C++11), концепты (C++20) — с каждым новым стандартом и витком развития язык предоставляет разработчику всё больше информации о том, что творится в недрах компилятора: начиная от манипуляции на уровне лексем и заканчивая compile-time-вычислениями и декларативным описанием свойств пользовательских типов. Логичное завершение (или продолжение?) этой цепочки — предоставление разработчику возможности напрямую манипулировать процессом компиляции кода на уровне AST. И такое предложение уже есть.

      Ох б%?№^& сука... ну охуеть теперь. Лучше сделайте новый язык, а этот выкиньте нахуй. Сделайте диалект лиспа с байтоебством и без GC. Я довольно хуево себе представляю то, каким образом с текущим крестоговносинтаксисом вы буделе делать манипуляции с AST
      Ответить
      • > диалект лиспа с байтоёбством и без GC
        И с UB'ами во время конпеляции!

        Но это уже будут не кресты. Вся суть крестов - прикручивать к ним новую хуйню не ломая совместимость со старой. Это как игра про башню из кирпичиков.
        Ответить
      • > манипуляции с AST
        В предложении про метаклассы это выглядело довольно просто - из constexpr блока можно позвать оператор -> {...} который высрет хуйню из скобок в родительский блок и раскроет в ней $переменные. Ну и оператор $, которым можно получить constexpr инфу о типе.

        Конечно как всегда с кучей ебанутых ограничений и костылей. Но они почти придумали лисповые макросы :)
        Ответить
        • З.Ы. В принципе, для решения практических задач типа сериализации, mock'ов и т.п. этого вполне хватит.
          Ответить
        • > Конечно как всегда с кучей ебанутых ограничений и костылей. Но они почти придумали лисповые макросы :)

          Или я нихуя не понял, или это говно никакого (или почти никакого) отношения к лисповым макросам не имеет.

          Вот представим себе некий гомоиконный язык, в котором нет циклов for но есть ite и goto. И допустим у нас будет функция, которая считает сумму чисел от 1 до 10
          ( (funcname sum1_10 (int) (void) )
          
            (newvar a (type int))
            (newvar sum (type int))
            (:= a 1)
            (:= sum 0)
            (jmplabel loop)
              (:= sum (+ sum a))
              (:= a (+ a 1))
            (ite   // if - then - else
              (<= a 10) (goto loop)
              () //nothing, end loop
            )
            (ret sum)
          )

          Ну и если захотим, можно впилить в язык цикл for, который будет трансформироваться в конструкцию с этим условием с goto и присвоением, типа вот:
          ( (funcname sum1_10 (int) (void) )
          
            (newvar a (type int))
            (newvar sum (type int))
            (:= sum 0)
            (for (:= a 1) (<= a 10) (:= a (+ a 1))
              (:= sum (+ sum a))
            )
            (ret sum)
          )

          и всё это достаточно легко будет делаться, мы просто рассматриваем нашу программу как связный список связных списков, больше нихуя нет, мы просто пишем код (макрос), который при вставке в код for вот с такими-то аргументами трансформируется/раскрывается вот в такой-то код, для этого "for" мы просто пишем код, который конструирует на основе аргументов какой-то новый код через связные списки связных списков и ... и все. Это - ПРОСТО.
          Когда же я читаю про метаклассы то это вообще хуйня какая-то. Классы какие-то, зачем мне вообще классы? Вот представим на минуту, что из крестов выпилили нахер for(), while() циклы, но зато есть if() и goto label, через эти метаклассы можно запилить for()? Если нет, при чем тут тогда лисповые макросы?
          Ответить
          • http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0707r3.pdf

            > A metaclass is written as a compile-time constexpr function that takes meta::type parameters, which are passed with reference semantics
            ...
            > In the latter form, it can be used to use the values or abstract state of objects of meta::type.
            ...
            > A pure base_class is a class that has no instance data, is not copyable, and whose a destructor is either public and virtual or protected and nonvirtual. Unlike an interface, it can have nonpublic and nonvirtual functions.
            ...

            блядь, сколько хуйни на ровном месте. Какие-то pure base_class, какие-то методы, классы, хуяссы, констэкспры, meta::type. ЗАЧЕМ? ЗАЧЕМ ЭТО ВСЁ? Как это соотносится с лисповыми макросами?
            Ответить
            • Они дали возможность заменить тело constexpr блока на сгенерённую этим блоком хуйню. До нормальных макросов было рукой подать. Но они вместо этого начали городить какую-то хуйню с метаклассами :(

              З.Ы. Я одну из первых версий пропозала читал, возможно сейчас там всё поменялось.
              Ответить
              • ШОК! Запрещенные секретные приемы C++ метапрограммирования с шаблонами, макросами и констэкспрами. Дедовский способ. Смотреть до конца!!!
                Ответить
                • Ученые открыли способ добавить гомоиконы в C++. Нужно всего лишь...
                  Ответить
                • показать все, что скрытоvanished
                  Ответить
                  • Наверняка будут какие-то особые случаи, где без препроцессорного программирования или тупо нельзя будет обойтись или слишком много метаклассо-шаблонного говна надо будет писать, который еще хрен поймешь и который будет компилироваться хер знает сколько
                    Ответить
          • Пока кто-то мечтает об обстрактных гомоиконах, в S" Forth" из слов потока управления достаточно:
            ahead —– отмечает безусловный переход вперёд;
            if —– отмечает условный переход вперёд;
            then —– разрешает переход вперёд;
            begin —– отмечает переход назад;
            until —– разрешает условный переход назад;
            again —– разрешает безусловный переход назад;
            cs-pick и cs-roll для работы со стеком потока управления.
            Всё остальное можно реализовать поверх них:
            : else  ( C: orig1 -- orig2 )
                postpone ahead  ( С: orig1 orig2 )  \ отметили переход вперёд
                1 cs-roll       ( C: orig2 orig1 )  \ поменяли ссылки на стеке местами
                postpone then   ( C: orig2 )        \ разрешили ссылку вперёд, 
            ; immediate
            
            : while  ( C: dest -- orig dest )
                postpone if  ( C: dest orig )  \ отметили переход вперёд
                1 cs-roll    ( C: orig dest )  \ поменяли ссылки местами
            ; immediate
            
            : repeat  ( C: orig dest -- )
                postpone again  ( C: orig )  \ разрешили переход назад
                postpone then   ( C: )       \ разрешили переход вперёд
            ; immediate
            
            
            .( test if else then:) cr
            : .sign  ( n -- )
                dup 0<
                if
                  ." negative"
                else
                  dup 0=
                  if
                    ." zero"
                  else
                    ." positive"
                  then
                then drop cr ;
            
            -42 .sign
            0 .sign
            100500 .sign
            cr
            
            .( test begin while repeat:) cr
            : .count  ( u -- )
                begin
                  dup
                while
                  dup .
                  1-
                repeat
                drop ." done." cr ;
            
            5 .count
            1 .count
            0 .count
            https://ideone.com/00PGzJ
            Ответить
    • > без расширения конпелятора
      > требует стороннюю утилиту
      Мне одному кажется, что это и есть расширение конпелятора?
      Ответить
      • Ну как бы эта утилита компилятором не является. Типа как PHP добавить в кресты как препроцессор - сам компилятор от этого никак не расширяется
        Ответить
        • Препроцессор не понимает синтаксис крестов. А тут именно расширение языка через жопу.
          Ответить
          • На PHP (если постараться) можно написать парсер/лексер плюсов. А на сишном препроцессоре - нет. Поэтому я за PHP
            Ответить
    • показать все, что скрытоvanished
      Ответить
      • Нет! А вдруг кончится компиляция!
        Ответить
        • Тогда придётся чем-нибудь подкармливать компилятор, чтобы досмотреть мультик. Дописывать говно в авральном режиме!
          Ответить
    • показать все, что скрытоvanished
      Ответить
      • показать все, что скрытоvanished
        Ответить
      • Ахахах, в S" Forth" ещё в 71 году можно было управлять процессом конпеляции и интерпретации. Там на этом любую идею из любых других языков реализовать можно (кстати, одна из реализаций ООП S" Mini-OOF" занимает всего 14 строчек).

        Божечки, почему программисты стабильно изобретают колесо и текут от этого?
        Ответить
        • Изобрел костыль тебе за щеку, проверь.
          Ответить
          • Проверил: костыль некачественный, конструкция ненадежная. Отправил назад на доработку. Проверь.
            Ответить
            • доработал за твоей щекой, проверь.
              Ответить
              • Зачем ты работаешь за петушиными щёками?
                Ответить
                • Рекурсионно поработал с петушиной щёкой за твоей щекой, проверь.
                  Ответить
                  • File "<stdin>", line 1, in work_behind_rooster_cheek
                      File "<stdin>", line 1, in work_behind_rooster_cheek
                      File "<stdin>", line 1, in work_behind_rooster_cheek
                      File "<stdin>", line 1, in work_behind_rooster_cheek
                      File "<stdin>", line 1, in work_behind_rooster_cheek
                      File "<stdin>", line 1, in work_behind_rooster_cheek
                      File "<stdin>", line 1, in work_behind_rooster_cheek
                      File "<stdin>", line 1, in work_behind_rooster_cheek
                      File "<stdin>", line 1, in work_behind_rooster_cheek
                      File "<stdin>", line 1, in work_behind_rooster_cheek
                      File "<stdin>", line 1, in work_behind_rooster_cheek
                      File "<stdin>", line 1, in work_behind_rooster_cheek
                      File "<stdin>", line 1, in work_behind_rooster_cheek
                      File "<stdin>", line 1, in work_behind_rooster_cheek
                      File "<stdin>", line 1, in work_behind_rooster_cheek
                      File "<stdin>", line 1, in work_behind_rooster_cheek
                      File "<stdin>", line 1, in work_behind_rooster_cheek
                      File "<stdin>", line 1, in work_behind_rooster_cheek
                      File "<stdin>", line 1, in work_behind_rooster_cheek
                      File "<stdin>", line 1, in work_behind_rooster_cheek
                      File "<stdin>", line 1, in work_behind_rooster_cheek
                      File "<stdin>", line 1, in work_behind_rooster_cheek
                      File "<stdin>", line 1, in work_behind_rooster_cheek
                      File "<stdin>", line 1, in work_behind_rooster_cheek
                      File "<stdin>", line 1, in work_behind_rooster_cheek
                      File "<stdin>", line 1, in work_behind_rooster_cheek
                      File "<stdin>", line 1, in work_behind_rooster_cheek
                      File "<stdin>", line 1, in work_behind_rooster_cheek
                      File "<stdin>", line 1, in work_behind_rooster_cheek
                      File "<stdin>", line 1, in work_behind_rooster_cheek
                      File "<stdin>", line 1, in work_behind_rooster_cheek
                      File "<stdin>", line 1, in work_behind_rooster_cheek
                      File "<stdin>", line 1, in work_behind_rooster_cheek
                      File "<stdin>", line 1, in work_behind_rooster_cheek
                      File "<stdin>", line 1, in work_behind_rooster_cheek
                    RuntimeError: maximum recursion depth exceeded
                    Ответить
    • > универсальный сериализатор структур, написанный с помощью hana:

      Тут проблема не в отсутствии гомоиконности, а в том, что только полному отморозку придёт в голову использовать в качестве схемы данных крестомакросню:

      – Как нам распарсить этот бинарный блоб, г-н Рахитектов?
      – Для начала чекаутни ревизию fd3fgsf4 нашего проекта и грепни на 'BOOST_HANA_DEFINE_STRUCT(Sosnoolee'

      Надо чтобы ASN.1!
      Ответить
      • В общем, ничего удивительного: глупые задачи приводят к глупым решениям.
        Ответить
    • показать все, что скрытоvanished
      Ответить

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