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

    +5

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    12. 12
    // https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern
    
    // The Curiously Recurring Template Pattern (CRTP)
    template<class T>
    class Base
    {
        // methods within Base can use template to access members of Derived
    };
    class Derived : public Base<Derived>
    {
        // ...
    };

    > The Microsoft Implementation of CRTP in Active Template Library (ATL) was independently discovered, also in 1995 by Jan Falkin who accidentally derived a base class from a derived class. Christian Beaumont, first saw Jan's code and initially thought it couldn't possibly compile in the Microsoft compiler available at the time. Following this revelation that it did indeed work, Christian based the entire ATL and Windows Template Library (WTL) design on this mistake.

    А какая ошибка по-вашему положена в основу всего дизайна языка C++?

    Запостил: j123123, 06 Февраля 2019

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

    • > The Microsoft Implementation of CRTP in Active Template Library (ATL) was independently discovered, also in 1995 by Jan Falkin who accidentally derived a base class from a derived class. Christian Beaumont, first saw Jan's code and initially thought it couldn't possibly compile in the Microsoft compiler available at the time.

      Может суть языка C++ это использование (открытие) каких-то ебанутых багофич, по поводу которых даже возникают сомнение, соберется ли вообще это говно, или нет?
      Ответить
    • показать все, что скрытоvanished
      Ответить
    • Шизофрения. Выдержка из интервью с Александром Степановым:
      "It was not any particular function, but the number of them. Bjarne is personally responsible for reducing the number of components in STL by a factor of two. He was trying to make it as small as possible to placate the opposition. "
      http://www.stlport.org/resources/StepanovUSA.html
      В с++17 добавили приснопамятные функции Бесселя.
      Главная проблема дизайна с++ - это желание угодить всем, что вытекает в очень противоречивые и политически ангажированные решения по поводу добавления новых фич.
      Ответить
      • Просто вы не знаете, что такое функции Бесселя, вот и беситесь.
        Ответить
      • Забавно, что среди популярных языков вырисовываются три основных подхода к созданию стандартных библиотек:
        1) JS-like: в стандартной библиотеке нет ровным счётом нихуя. Однако присутствует мощный и крайне простой пакетный менеджер с большим архивом библиотек, для использования которого достаточно просто написать «import».
        2) Python-like: в стандартную либу напихано огромное количество модулей на любые случаи жизни; сторонние библиотеки нужны гораздо реже и только для сильно специфических (по сравнению с предыдущим пунктом) задач.
        3) ГовноC++-like: в стандартной либе нет почти нихуя (а то, что есть — неюзабельное говно). При этом пакетного менеджера тоже нет и не предвидится, а установка сторонних библиотек — ёбанный ад.
        Ответить
        • См. https://govnokod.ru/20044
          Ответить
          • Тогда нужно добавить:
            4) Вроде пакетный менеджер есть, но всё сломано и нихрена не ставится (Haskell-like).
            Ответить
            • если есть CMake то собрать может даже ребенок. Но я против егой ебалы типа С++. C++ модули решают проблему но только тем что прячут проблему от нас, но не решают ее. Я давно говорил что все языки должны компилить в независимый байткод и тогда уже юзаться любым компайлером
              Ответить
    • сам дизайн - ошибка. а использование С++ в реальных проэктах и подавно.
      https://www-users.cs.york.ac.uk/susan/joke/cpp.htm
      Ответить
    • «Java» перенимает лучшее:
      public static class Builder<SELF extends Builder<SELF>> {
      
          SELF firstName(String value) {
              this.firstName = value;
              return (SELF) this;
          }
      
      // ...
        public static class Builder extends User.Builder<RussianUser.Builder> {  //...

      s: https://habr.com/ru/company/jugru/blog/438866/
      Ответить
    • {kokoko
      Ответить
    • {koko
      {ko
      Ответить
    • kokoko{
      Ответить
    • kokoko{kokoko
      Ответить
    • показать все, что скрытоvanished
      Ответить
    • {
      Ответить
    • }
      Ответить
    • kokoko}kokoko
      Ответить
    • короче С++ гавно. Уберите * и все станет на свои места. сделайте модули, сделайте "поздний линкер" при запуске и тогда поговорим
      Ответить
      • >> сделайте "поздний линкер" при запуске

        DLL/so чем не поздний линкер?
        Ответить
      • >> Уберите *

        Запросто. *a является синонимом a[0]. Можно явно писать [0] вместо звёздочки.
        Ответить
      • > Уберите *
        #define PTR *
        char PTR s;
        putchar(PTR s);
        
        // или
        
        #define PTR(x) *x
        char PTR(s);
        putchar(PTR(s));
        
        // ещё можно
        
        #define PTR_T(t) typedef t *t##_ptr; t##_ptr
        PTR_T(char) s;
        putchar(PTR(s));
        Ответить
        • Ещё можно так:
          putchar(s[0]);

          И даже так:
          putchar(0[s]);
          Ответить
          • Именно поэтому я за "Си".
            Ответить
          • в первом случае ты к адресу s прибавил 0
            а в другом к нолю прибавил s

            от перестановки мест слагаемых...
            Ответить
            • В "JS" коммутативность не всегда работает. Именно поэтому я за против "JS".
              Ответить
            • https://ideone.com/pthjYk
              Ответить
            • Вот кстати, я долго не мог научиться работать с указателями в сишке. Потом наконец вдруг понял, что сишка не различает указатель на чар и указатель на массив чаров. Не существует не только способа ограничить размер массива, передаваемого по указателю, но и даже отличить массив от скаляра. После «Паскаля» это было дико.

              Не по этой ли причине некоторые боятся звёздочки в сишке?
              Ответить
              • Потому и не различает, что нет массивов. Их роль играют указатели.

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

                  Для кокококонсистентности надо было вместо структур тоже сделать указатели.
                  Ответить
                  • Массивы в структурах очень хорошие:
                    struct pityx {
                        size_t putyh_len;
                        char pitux_data[0];
                    };
                    Ответить
                    • Variable length array.

                      А теперь переставь поля местами.
                      Ответить
                      • Такие структуры могут быть полезны, например, для чтения каких-нибудь форматов файлов, я такое много раз видел. Только я не понял, как такое может сочитаться с паддингом.
                        Ответить
                        • Паддинг важен для sizeof и для размещения переменной в памяти (чтобы выделить под неё место), а также для вычисления зазоров между полями.

                          Зазоров не будет, если size_t совпадает по размеру с паддингом (обычно достаточно кратности машинному слову) или если указаны директивы для упаковки структуры (#pragma, __attribute__ –— вот это всё).

                          Что же касается паддинга в самом конце, то обычно такие структуры применяют к уже выделенной памяти, поэтому на sizeof нам плевать.

                          Но вообще да, какое-то чувство незавершённости остаётся.
                          Ответить
                        • В «Windows» такие структуры используются для возвращения инфы о файлах, например, функцией NtQueryDirectoryFile: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ntifs/ns-ntifs-_file_directory_information. Особый цимес в том, что функция эта возвращает нечто вроде связного списка структур переменного размера (в котором роль указателя на следующий элемент играет размер текущей структуры в байтах, блядь), в результате чего работа с ней превращается в жуткое траханье мозгов.
                          Ответить
                          • Зато сериализовать просто, поскольку вместо указателей используются относительные смещения.
                            Ответить
                          • P.S. Как принято определять размер FileName: она zero-terminated, как все строки в сишке, или вычитать из NextEntryOffset суммарный размер всех предыдущих полей?

                            P.P.S. Торможу, есть же FileNameLength.
                            Ответить
                          • > роль указателя имеет размер
                            Запиливаешь шаблонный итератор и хоть няшным с++11 for'ом по этой хуйне бегай.

                            Это к слову о том, почему я люблю кресты для low-level.
                            Ответить
                • показать все, что скрытоvanished
                  Ответить
              • показать все, что скрытоvanished
                Ответить
                • >> char petuh[42] -- реально 42 чара занимает.
                  Верно. Есть даже такой прикол:
                  int foo() {
                      char petuh[42] = "Ololo"; // реально разместит в стеке 42 элемента.
                  
                      char *kurochka = "Trololo"; // может разместить где угодно
                                                                  // например, в секции .data, а сюда вставит адрес первого элемента
                  }

                  Так что в некоторых контекстах массивы действительно есть.

                  Но вот конструкции вроде [42]petuh, petuh+42 (и для «petuh», объявленного как указатель, и для «petuh», объявленного как массив) и отсутствие разделения указателя на массив и указателя на скаляр сбивают с толку новичков.

                  Конструкции *petuh и petuh[0] работают одинаково, причём всё в силе как для char *petuh, так и для char petuh[42].
                  Ответить
                  • показать все, что скрытоvanished
                    Ответить
                  • > char *kurochka = "Trololo"
                    Выбрось свой древний конпелятор. Он протух.

                    З.Ы. Хм, странно, в сишном режиме даже ворнингов нету на эту херню...
                    Ответить
                    • показать все, что скрытоvanished
                      Ответить
                      • Не заставляет писать const (чтобы не сломать старые проги?) но при этом писать в получившийся указатель давным-давно нельзя (т.е. эти старые проги потенциально могут пиздануться, но ты об этом не узнаешь до запуска). Нахуй так жить?
                        Ответить
                        • показать все, что скрытоvanished
                          Ответить
                          • В старом gcc (3.x?) был ключик -fwritable-strings, которым можно было возродить древнее зло и невозбранно срать в литералы...
                            Ответить
                            • показать все, что скрытоvanished
                              Ответить
                              • Да в защищённом тоже можно. Подкрути атрибуты секции и радуйся. Ну или в рантайме попроси ось поменять защиту на странчиках. Правда конпелятор мёржит одинаковые литералы (но не обязан). Будет весело.
                                Ответить
                                • Я верно понимаю что загрузчик ОСы по умолчанию секцию .data (ну или как она там называется в разных осах) грузит в страницы с ключом R/O, и (по крайней мере в x64) такие страницы хардварно защищены от записи?

                                  А как поменять ключ секции? Линкером?

                                  Как поменять страницу я понимаю, хотя это и не оч секурно
                                  Ответить
                                  • Конпелятору можно сказать: "высри вот эти функции и переменные вот в эту секцию".
                                    Линкеру можно сказать: "навесь на эту секцию вот такие атрибуты".
                                    Загрузчик мапает секции так, как ему сказал линкер.
                                    Ответить
                                    • копулятору через прагму и атрибуты говорят?
                                      Ответить
                                      • Да. Т.е. можно какой-то конкретный массив сделать rwx если хочется.
                                        Ответить
                                        • показать все, что скрытоvanished
                                          Ответить
                                          • > как
                                            Блин, посмотри в доке от соответствующего копулятора. Я флешку паяю, некогда гуглить.

                                            Можешь готовую секцию взять (и подкрутить ей атрибуты при желании). Можешь в новую высрать.
                                            Ответить
                                          • > у винды
                                            > у прыщей

                                            Ну и терминология!

                                            У «gcc» ты можешь указать имя секции, в которую совать переменную. А вот секцию создаёт линкер. Вероятно, ему нужно скормить объектный файл, в котором эта секция уже создана ассемблером.
                                            Ответить
                                            • показать все, что скрытоvanished
                                              Ответить
                                              • Тут не только в ОС дело, а ещё в кококомпиляторе. «Linux» –— это не только «gcc», а «Windows» —– это не только «MSVC».

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

                                                В общем, нужно посмотреть две вещи:
                                                1. Какие прагмы есть в разных компиляторах.
                                                2. Какие секции поддерживает загрузчик ОС.

                                                *****

                                                Виндовому загрузчику PE-файлов, если не ошибаюсь, на количество и названия секций насрать. Он смотрит атрибуты каждой секции. Ты можешь, например, создать секцию PETUH с атрибутами R-X и секцию KUROCHKA с атрибутами RW.

                                                *****

                                                В «a.out» количество секций фиксировано: есть .text (r-x, инициализированная), .data (rw, инициализированная), .bss (rw, неинициализированная) с заранее известными всем атрибутами. То есть даже для кокококонстант секции нет.

                                                Если важно защитить константы от исполнения, их кладут в секцию .data (насрав на отсутствие защиты от записи). Если важно защитить константы от записи, их кладут в секцию .text (насрав на отсутствие защиты от исполнения).

                                                *****

                                                В «ELF», как и в «PE», можно создать 100500 секций с произвольными атрибутами.
                                                Ответить
                                                • Сейчас посмотрим, какие ещё бывают форматы.

                                                  У «XCOFF» количество секций произвольно, однако, произвольные атрибуты выставлять нельзя, можно выбирать только стандартные шаблоны (секция типа «.text», секция типа «.data») и т. п.

                                                  У «PEF» («Mac OS Classic») такая же ерунда, однако, есть секция для констант, а у «XCOFF» я её не нашёл.

                                                  У «Mach-O» («Mac OS X») похоже, что тоже стандартный набор шаблонов, причём шаблоны привязаны к названию. Шаблонов стало больше, но свои атрибуты секции создавать нельзя. Нужно искать секцию с подходящим названием.

                                                  У «NE» (16-битные приложения «Windows» и «OS/2») можно создавать произвольное количество секций с произвольными атрибутами (разрешения на чтение, запись, выполнение выставляются отдельно, как и у «PE» и «ELF»).
                                                  Ответить
                                                  • Итого можно выделить три типа платформ:
                                                    1. Можно создать секцию с произвольными атрибутами: «ELF», «Windows», «OS/2».
                                                    2. Можно создать секцию, но из заранее заготовленных шаблонов: системы типа «Макоси».
                                                    3. Можно только выбирать из готовых секций: «a.out».
                                                    Ответить
                                  • >> и (по крайней мере в x64) такие страницы хардварно защищены от записи?

                                    В x64 они дополнительно защищены от исполнения. В x32 без DEP они защищены только от записи.
                                    Ответить
                              • Разве что на микроконтроллерах реально нельзя писать в литералы.

                                З.Ы. Хотя на тех же stm'ках можно снять блокировку с флеша и менять единички на нолики...
                                Ответить
                            • А можно объявить один мутабельный литерал?
                              Ответить
                  • > [42]petuh
                    Вы про 42[petuh]?
                    Кстати, зачем эта питушня нужна? Не могу придумать ситуации, где бы она понадобилась. (Реальной ситуации, а не заседания клуба задротов или "креативных" вопросов на собеседованиях)
                    Ответить
                    • ну там же просто под копотом копулятор делает

                      "оффсет петуха поюс 42"
                      или
                      "42 плюс оффсет питуха"
                      и все


                      не то чтобы ее как-то специально делали
                      Ответить
                      • > не то чтобы ее как-то специально делали
                        почему оно тогда во всех компиляторах работает?
                        Ответить
                      • Там же, в отличие от malloc, статическая питуизация учитывает размер пера:
                        "оффсет петуха плюс 42 на размер элемента петуха"
                        или
                        "42 плюс оффсет питуха на размер элемента 42"
                        А это уже не симметричная питушня.
                        Ответить
                        • Когда складываются указатель и число, на размер умножается число, вне зависимости от порядка опердандов.
                          Ответить
                        • показать все, что скрытоvanished
                          Ответить
                        • Я понял, как это объяснить. В данном случае «+» является перегруженным оператором (перегрузка в том смысле, как в «C++»). Перегружен он для ситуации, когда один аргумент является указателем, другой целым числом. Тогда именно целое число умножается на размер элемента (если указатель типизированный; для void * же такая операция не имеет смысла).

                          Для других комбинаций типов слагаемых аналогичной перегрузки нет (есть только сложение скаляров), поэтому к неоднозначности это не приводит.
                          Ответить
                      • Самое страшное место сишной арифметики указателей: одно из слагаемых нужно умножить на размер элемента массива. Компилятор должен угадать, какое из слагаемых является элементом, а какое –— индексом, чтобы кодогенератор выдал
                        petuh + 42 * sizeof(petuh[0])
                        , а не
                        42 + petuh * sizeof(42)
                        Ответить
                    • Я нашёл этому применение, можно в случае кастования писать меньше скобок:
                      // https://tio.run/##ZcpNCsIwEEDh/ZxiwEUTQfF3FetFShdlEuiApsFOpFA8e9TZZvu@RzshKmXDkR7ZB7zN4nnaj3eA98QeUxBeumvvADgKPgeO5g8WVkA8dIbG4bW1uvXYYpMa94NjDUHhVIMonGvICpcaFoWUZTYarYNPKV8
                      
                      #include <stdio.h>
                      
                      void petix[5];
                      
                      int main(void)
                      {
                        0[(char*)petix] = 'p';
                        1[(char*)petix] = 'e';
                        2[(char*)petix] = 't';
                        3[(char*)petix] = 'u';
                        4[(char*)petix] = 'x';
                        puts(petix);
                      }
                      Ответить
                  • показать все, что скрытоvanished
                    Ответить
                    • показать все, что скрытоvanished
                      Ответить
                      • Именно поэтому я за «JS». Он всегда закрывает строчку за меня. Написал вместо return 42; так:
                        return
                        42;
                        Он автоматически закрыл строчку:
                        return;
                        42;
                        Вернул void, а 42 стало unreachable.
                        Ответить
                        • Именно поэтому я за «Ruby». Он всегда за меня и ретурн пишет и точку с зопятой

                          def foo
                            42
                          end
                          
                          
                          p foo #42
                          Ответить
                  • показать все, что скрытоvanished
                    Ответить
                • >> хотя наверное я не стал бы передавать массив размером в гигабайт -- лучше передать все таки указатель и рядышком размер

                  Сравним, как сделали в «Турбо Паскале», «Delphi» и в более поздних проектах вроде «FPC».

                  1. Если в функцию передаёшь массив, то пушится не сам массив, а указатель на его копию в стеке («копирующий конструктор»; если передаёшь литерал, то он где-нибудь создаётся и тоже передаётся указатель). Но точно так же он поступает и с другими огромными данными вроде структур.

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

                  Если тип параметра является статическим массивом, то размер передавать не нужно, он известен на этапе компиляции.

                  Есть более сложные сущности: открытые массивы (array of integer, например), динамические массивы и array of const. В этом случае после указателя на массив компилятор пушит ещё его размер.

                  2. Если же вызываемая функция должна вернуть массив или структуру, то он выделяет место в стеке под результат и самым последним аргументом передаёт указатель на это место. Т. е. результат живёт там же, где живут локальные переменные вызывающего кода (никакой возни с ручным выделением/освобождением).

                  Да, эти два приёма по сути являются хаками. Однако, структуры и массивы обрабатываются единообразно.
                  Ответить
      • В Java и С# уже убрали. Получилось лакированное говно.
        Ответить
        • Тащем-та в C#/CLR аж 3 типа указателей. Managed pointer (для ref/out), native pointer (синтаксис такой же, как в C++: int*) и opaque pointer (IntPtr)
          Ответить
          • показать все, что скрытоvanished
            Ответить
            • Как light user data в Lua (кажется, такой там термин). Нам не важно, что там внутри, это просто какой-то указатель на что-то. Полезно при создании обёрток вокруг сишных/C++'овых библиотек. Допустим, CreateButton() возвращает Button*, и все действия в оригинальном API происходят через функции вот так: SetButtonText(Button*, ...) Тогда нам не важно, что это за конкретно тип и что там внутри, можно просто описать функции, как возвращающие IntPtr и принимающие IntPtr.
              Ответить
    • Именно поэтому я за «C++».
      Ответить
    • показать все, что скрытоvanished
      Ответить

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