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

    +1

    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
    #include <iostream>
    
    template<typename T>
    struct CrtpBase {
        static float base_func()
        {
            std::cout << "CrtpBase::base_func(), " << typeid(T).name() << "::int_field == "
                << T::int_field << std::endl;
            return 16.0f;
        }
    
        static inline float x = base_func();
    };
    
    struct A: public CrtpBase<A> {
        void func_a()
        {
            base_func();
        }
    
        static inline int int_field = 16;
    };
    
    struct B : public CrtpBase<B> {
        void func_b()
        {
            std::cout << "B::func_b(), no CrtpBase<B>::base_func() call" << std::endl;
        }
    };
    
    
    int main()
    {
        A a;
        a.func_a();
    
        B b;
        b.func_b();
    }

    [temp.inst]/2:
    Unless a class template specialization is a declared specialization,
    the class template specialization is implicitly instantiated when
    the specialization is referenced in a context that requires a
    completely-defined object type or when the completeness of the
    class type affects the semantics of the program.

    [temp.inst]/3:
    The implicit instantiation of a class template specialization causes
    (3.1) -- the implicit instantiation of the declarations, but not of the definitions, of
    the non-deleted class member functions, member classes, scoped member enumerations,
    static data members, member templates, and friends; and
    (3.2) -- the implicit instantiation of the definitions of deleted member functions,
    unscoped member enumerations, and member anonymous unions.

    [temp.inst]/4:
    Unless a member of a templated class is a declared specialization, the specialization
    of the member is implicitly instantiated when the specialization is referenced in a
    context that requires the member definition to exist or if the existence of the definition
    of the member affects the semantics of the program; in particular, the initialization
    (and any associated side effects) of a static data member does not occur unless the
    static data member is itself used in a way that requires the definition of the static
    data member to exist.


    Таким образом, по стандарту CrtpBase<B>::x не должен быть инициализирован, поскольку он нигде не used in a way that requires the definition of the static data member to exist. Правильные компиляторы (gcc и clang) это понимают и компилируют код, а вот Visual Studio зачем-то пытается инициализировать CrtpBase<B>::x и нон-конформно ломается.

    Запостил: PolinaAksenova, 24 Марта 2021

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

    • Ух ты, скоро выучим крестоштандарт всем говнокодом!
      Ответить
    • Именно поэтому я за Си - там нет никаких "template" и никаких "class". И не нужно ломать себе мозг всяким бредом.
      Ответить
      • Скучно у вас в сишке, почти не о чем посраться до усрачки.
        Ответить
        • В кресты добавили столько ебанутой хуйни для того, чтобы потом сраться про нее?
          Ответить
          • Почему "потом"?
            Ответить
          • В пхп добавили столько ебанутый хуйни, что поэтому я за ппх.
            Ответить
        • У них просто после исправления всех бэд_аксессов и переполнений буфера на срачи не остаётся сил
          Ответить
          • Будто б в крестах решили проблему бэд_аксессов и переполнений буфера.
            Ответить
          • после такого кода
            struct group grp = create_group(&usr);
            if (!grp) {
              delete_user(&usr);
              return ERROR_USER;
            }
            struct petuh pth = create_petuh(&usr, &grp);
            if (!ptr) {
              delete_group(&grp);
              delete_user(&usr);
              return ERROR_PETUH;
            }
            Ответить
            • #define CREATE_GROUP_CHK(var, usr) \
              create_group(&usr); \
              if (!var) { \
                delete_user(&var); \
                return ERROR_USER; \
              }
              
              #define CREATE_PETUH_CHK(var, usr, grp) \
              create_petuh(&usr, &grp); \
              if (!var) { \
                delete_group(&grp); \
                delete_user(&usr); \
                return ERROR_PETUH; \
              }
              
              struct group grp = CREATE_GROUP_CHK(grp, usr);
              struct petuh pth = CREATE_PETUH_CHK(pth, usr, grp);
              Ответить
              • Остроумно, только код-то никуда не ушел. Просто он стал теперь обмазаный макроговном, и потому у него подсветка синтаксиса на говнокоде сломалась:)

                А в RAII это из коробки:) Но разумеется не бесплатно, а ценой охулиона всяких интересных "вопросиков"
                Ответить
                • > Остроумно, только код-то никуда не ушел.

                  А куда он в "RAII" уходит? Он же в каком-то там деструкторе тоже будет.
                  Ответить
                  • Ну имеется в виду, что O(n^2) не ушёл из исходника.
                    Ответить
                  • вот такого кода не будет:

                    delete_group(&grp);
                    delete_user(&usr);



                    Рантайм сам последовательно вызовет деструкторы в порядке, обратном вызову констуркторов.
                    Ответить
                    • Тогда можно взять __attribute__ (( cleanup (cleanfunc) )) как вот тут: https://govnokod.ru/26947#comment574989 https://govnokod.ru/25526

                      Выше по треду там есть ссылка на вариант без гнутого расширения https://govnokod.ru/24517
                      Ответить
                      • можно, но это не интуитивно
                        Ответить
                        • Более того, это совершенно неудобно и нерасширяемо, поскольку, во-первых, управлять таким образом можно исключительно указателями, а во-вторых, для каждой новой переменной нужно делать новый блок.

                          А код с гнутыми расширениями — говно по умолчанию, поскольку не соответствует стандарту.
                          Ответить
                          • > Более того, это совершенно неудобно и нерасширяемо, поскольку, во-первых, управлять таким образом можно исключительно указателями

                            Почему это? Можно такое и на файловый дескриптор типа int навесить, чтоб вызывалось close() по выходу.
                            Ответить
                            • Потому что
                              struct raii_lifetime_elem {
                                  void *resource;
                                  void (*destructor)(void *);
                              };

                              .
                              Ответить
                              • А разве деструктор в крестах не через указатель на экземляр класса работает?
                                Ответить
                                • Эм, нет. Просто вызывается в нужных точках как обычная функция:
                                  {
                                      foo f;
                                      // позвался foo::foo(), сишный аналог -- init_foo(&f);
                                      // ...
                                      // позвался foo::~foo(), сишный аналог -- cleanup_foo(&f);
                                  }
                                  Ответить
                                  • Допустим для raii над файловыми дескриптором надо вызывать close по выходу из скоупа. Чтоб вызвать close применительно к переменной int внутри соответствующего класса, разве деструктор не должен неявно получать указатель на экземпляр класса?
                                    Ответить
                                    • > деструктор не должен неявно получать указатель на экземпляр класса

                                      Должен, но он же просто адрес локалки получает в данном случае. Ну вот как-будто бы ты в сишке позвал cleanup_foo(&f) для какой-то структурки, которая у тебя на стеке валяется (а в ней файловый дескриптор).
                                      Ответить
                                      • >указатель
                                        так вот жи:
                                        >&f

                                        лол)

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

                                        Ну логично
                                        Ответить
                                • Почему?
                                  int main() {
                                    Petuh p;
                                  } //тут где-то вызовеца деструктор, вроде ни одного указателя не пострадало


                                  Указатель обязателен для полиморфизма и вирт методов, ну и для работы с кучей само собой.

                                  А без этого можно вообще про указатели ничего не знать кмк
                                  Ответить
                                  • > } //тут где-то вызовеца деструктор, вроде ни одного указателя не пострадало

                                    Можно написать и с указателями, и компилятор может соптимизировать так, чтобы реально никаких указателей не было.
                                    Ответить
                          • > а во-вторых, для каждой новой переменной нужно делать новый блок.

                            Что значит "для каждой новой переменной нужно делать новый блок"?
                            Ответить
                            • То и значит.
                              raii_with(int *var_a, malloc(16), free) {
                                  raii_with(int *var_b, malloc(16), free) {
                                      raii_with(int *var_c, malloc(16), free) {
                                      }
                                  }
                              }
                              Ответить
                              • А, речь про raii_with... ну можно еще слой костылей сделать, чтобы этот новый блок незаметно создавался. Для __attribute__ (( cleanup (cleanfunc) )) такого не надо
                                Ответить
                          • > поскольку не соответствует стандарту

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

                            Всё лучше, чем городить хрупкий лес из костылей и тонкостей стандарта.
                            Ответить
                            • Один хрен, как только ты выбираешься из уютного лампового мирка машины абстрактной в жестокий и беспощадный мир машины реальной, копеляторозависимая магия вылазит. Особенно, когда надо обеспечить взаимодействие нескольких программ. Ну нет стандартного способа заставить использовать конкретное соглашение о вызовах. Нет способа жестко зафиксировать, как члены структуры лежат в памяти (а постоянно гонять байты туда-сюда чтобы поймать момент, когда другой процесс покопается в памяти, неперфомансно). Стандартных динамически загружаемых библиотек тоже нет...
                              Ответить
                              • Это скорее системозависимая магия. Компилятор dlsym() в GetProcAddress() транслировать не умеет.
                                Общение между программами через расшаренную память (которая тоже системозависимая, да ещё и неоднозначно транслирующаяся, чёрт бы побрал эти CreateFileMapping()+MapViewOfFile()) — это какой-то мазохизм, в обычной прикладухе он не нужен (обычная прикладуха спокойно гоняет данные через локальные сокеты… которые тоже до сих пор не стандартизированы).
                                Ответить
                                • А threads же стандартизировали уже?
                                  Когда-нить и до остального доберутся.
                                  Ответить
                                  • Стандартизировали в C++11, но криво и косо. std::jthread только в 20-й версии сделали, десять лет трудились над этой непосильной задачей.
                                    Ответить
                                    • У комитета паническая боязнь предпочесть какое-то конкретное решение какому-то другому. Поэтому стандартизируют одинакого далеко от всех реально используемых. Но абстрактно и расширяемо.
                                      Ответить
                                      • Вспоминается эпопея с числом пи: его пытаются стандартизировать уже четыре года, за это время сделали ВОСЕМЬ версий пропозала — http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0631r8.pdf — а крестовики до сих пор делают рожу как на https://i.kym-cdn.com/entries/icons/original/000/003/617/OkayGuy.jpg и вставляют constexpr double PI = 3.141592653589793 в каждый хедер с математикой.
                                        Ответить
                                        • Типичная стандартизация. А какие константы нужны? А каких типом? А давайте сделаем их шаблонами переменных, чтобы их можно было использовать как pi<long double>! А куда их засунуть? А как новый хедер назвать? О боже, если пользователь обновится на новый стандарт, откроет файл, в котором он творил нерекомендованную хуйню, включит заголовочный файл, который до этого стандарта не существовал, то код не скомпилируется! Нужно не дать пользователю стрелять в ногу и не заставлять его думать! Давайте запихнём краткие константы, введённые для удобства, в неймспейс, непременно с названием длиннее, чем сами значения констант!
                                          Ответить
                    • > Рантайм сам последовательно вызовет деструкторы в порядке, обратном вызову констуркторов.

                      Что значит "рантайм"? Разве для этого нужен какой-то "рантайм" со стороны крестов? Это можно тупо препроцессором каким-то накостылить, что если переменная выходит из скоупа, то вызываем такую-то хуетень.
                      Ответить
                      • > Разве для этого нужен какой-то "рантайм" со стороны крестов?

                        Нет конечно, это ж не джава какая-нибудь... У меня нет никакого рантайма, а RAII работает. Просто конпелятор сам расставляет вызов деструкторов в точках выхода.
                        Ответить
                        • > у меня нет никакого рантайма.

                          The program can't start because vcruntime140.dll is missing from your computer. Try reinstalling the program to fix this problem.
                          Ответить
                          • Мы как-то терли про сборку програмы под виндой без CRT. Это вполне реально, но экономия минимальна
                            Ответить
                            • > экономия

                              Ради экономии это делать глупо, да. Но есть и другие причины, например операционка -- это ты сам. Некому сисколлы кидать.
                              Ответить
                              • Лайфхак — на не-hosted системах половина Стандарта отключается и можно творить разную ересь.
                                Ответить
                                • > можно творить разную ересь

                                  Угу, к примеру вызов 16-битного прерывания через 64-битный интерфейс, переданый по шаред поинтеру.
                                  Ответить
                                  • > 16-битного прерывания
                                    Кстати, efi через CSM зарботал, спасибо.

                                    Я тут еще вспомнил, что каждая карта должна уметь VGA вообще без всякого VBE, так что сделать универсальный драйвер всё таки можно.

                                    Стандартнаые (не X) режимы позволяют иметь до 640x480 на 16 цветов, а 320x200 даже в 256 (тот самый 13h вроде)

                                    Просто видимо уефи думает, что 320x200 маловато по современным меркам
                                    Ответить
                                    • > каждая карта должна уметь VGA

                                      Не уверен про современные карты.

                                      А по uefi спеке все машины с ним обязаны поддерживать как минимум 800х600 емнип.
                                      Ответить
                                      • >Не уверен про современные карты.
                                        То есть гоблины первые у меня не заработают в досе?
                                        Ответить
                              • То есть ты пишешь на такой мелкой хрени, у которой даже CRT не реализован?

                                Не очень понятно причем тут выпил CRT из винды тогда)
                                Ответить
                                • А что ты понимаешь под CRT?

                                  Compiler-support library?
                                  Unwinder?
                                  Аллокатор?
                                  Обёртки над сисколлами?
                                  Всякие memcpy и strcpy?
                                  Крестолибу?

                                  Какие-то важные части запилены, конечно. Если они имеют смысл для не-hosted окружения.
                                  Ответить
                                  • то, что вызывает мой main(), и что позволяет мне дерать фнкции сишкиной библиотеки вплоть до malloc

                                    Хотя вот например динамическая память может и не иметь смысла для какой-то мелкой пижни где ты -- единственная прогорамма
                                    Ответить
                      • не, не нужен
                        ты прав
                        Ответить
              • Заставь сишника писать обобщённый код — он и \ расшибёт.
                Ответить
            • А я тупо goto fail юзаю в таких ситуациях.
              Ответить
              • всё равно уныло же
                Ответить
                • Да и похуй, зато не писать эти нарастающие столбики из delete_xxx. С goto по крайней мере O(n) а не O(n^2) по объёму кода.
                  Ответить
                  • Да, но мы-то изначально сравнивали с крестами
                    Ответить
                    • Там вон defer завезли. Можно его юзать если не задумываться из какого говна и палок он сделан.
                      Ответить
                      • как кукарекнул петух на хакерньюс:
                        "Moving to C++ seems wiser than using a dubious hack like this."
                        Ответить
                        • Я б лучше на D с better-c переходил, а не на ублюдские кресты.
                          Ответить
                          • Фу, там GC пахнет.
                            Хотя вроде бы можно и отказаться от него
                            Ответить
                            • Запустил как-то мужик StW GC,
                              Ответить
                              • Ну мы тут обычно обсираем GC не столько за stop the world (хотя ничего хорошего в этом тоже конечно нет) а больше за невозможность закрыть ресурс в деструкторе и все эти "with" и "Closable"
                                Ответить
                            • better-c это и есть "отказаться от него"
                              Ответить
                              • А чем он better, чем C?
                                Ответить
                                • Там вот написано https://dlang.org/spec/betterc.html

                                  Неймспейсы, миксины можно использовать, RAII есть, switch по строкам еще...
                                  Ответить
                                  • А если вы штатские все такие умные, то почему в столовую строем не ходите?

                                    А если "D" настолько охуенен, то почему его никто не использует?
                                    Ответить
                                    • Может потому что D в целом пошел по неправильному пути впиливания GC в рантайм, и вариант "D без GC" это какая-то немейнстримная фигня, под которую стандартная библиотека D даже не работает, но можно сишную юзать.
                                      Ответить
                                      • Может быть

                                        Я просто совершенно однозначно могу сказать, что еще один язык с GC точно не нужен. Их и так охулион и маленькая тележка
                                        Ответить
                            • А в крестах пахнет RTTI и эксепшенами, так что теперь?
                              Ответить
                              • Эксепшены иногда можно использовать с пользой, но чаще конечно нет.

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

                                  Ну и любой dynamic_cast<> — это уже RTTI так-то.
                                  Ответить
                                  • У тебя редкий кейс кмк. В общем случае знать тип в рантайме может же быть накладно (хотя во всяких джавах это отлично делают)
                                    Ответить
                                    • Тип всех объектов виртуальных классов и так в рантайм вшит, без него dynamic_cast не заработает же. Хотя да, если динамические касты не нужны, то можно RTTI и выключить.
                                      Ответить
                                      • Ну вот я не уверен, что dynamic_cast это прямо must have (хотя в некоторых языках и приветствуется чуть ли не свитч по типу) и вроде как все компиляторы имеют отпцию чтоб rtti выпилить

                                        Самому Страуструпу он не очень нравится емнип
                                        Ответить
                                • std::any
                                  Ответить
                                  • Ну не на публике же, стыд нужно иметь!
                                    Ответить
                                  • > std::any

                                    "void *" хватит всем.
                                    Ответить
                                    • void * — это совсем про другое.
                                      Ответить
                                      • Я знаю. std::any это какая-то невнятная фигня, которая как-то там хранит описание типа, которое можно в рантайме разбирать. A "void *" - просто указатель на некую хрень, и поэтому никаких там накладных расходов нет. Можно сделать структуру с "void *" и какой-то фигней, описывающей тип того, что в "void *" более компактно, чем это в std::type_info упихано в крестолибе.
                                        Ответить
                                        • > "void *" и какой-то фигней, описывающей тип того, что в "void *"
                                          и тут мы снова возвращаемся к моему любимому
                                          struct nevedomaja_jebanaja_huina {
                                            void* data;
                                            int type;
                                          }

                                          збс
                                          Ответить
                                        • Ну да, можно и Солнце вручную закатить, и каждый новый тип в енумчик прописывать, и свитч в деструкторе не забыть сделать. Но зачем, если язык может сгенерировать всю эту лапшу без участия программиста?
                                          Ответить
                                        • Ну std::any это же и есть просто структурка из void* + type_info*. Куда компактнее, разве что на выравнивание забить и хранить тип в uint8_t или вообще в битах указателя?

                                          А type_info оно один раз на класс, не на объект. Так что если сильно не метушить и не создавать тысячи типов, то оно не так уж много занимает. Тем более линкер выбросит лишнее, если ты не юзаешь.
                                          Ответить
                                          • > хранить тип в uint8_t
                                            Это std::variant получается. Все типы, что у тебя в программе присутствуют, в 8 бит не запихнёшь. А если всю шаблонометушню посчитать, то и в 16 не факт, что уложишься.
                                            Ответить
                                            • > Все типы, что у тебя в программе присутствуют, в 8 бит не запихнёшь.

                                              256 типов хватит для любого сишника.
                                              Ответить
                                              • Единственный нужный тип — массив интов.
                                                Ответить
                                                • Царь, ты?!
                                                  Ответить
                                                  • Для царя какой-то очень вежливый.. никого даже анскильным питухом не назвал
                                                    Ответить
                                                  • Родила царица в ночь
                                                    Не то сына, не то дочь;
                                                    Не мышонка, не лягушку,
                                                    А неведому крестушку.
                                                    Ответить
                                                    • Гост, перелогинься.
                                                      Ответить
                                                      • А вдруг это не Гост, а Полина Аксёнова?

                                                        Или даже так: а вдруг Гост это и есть Полина Аксёнова?
                                                        Ответить
                                                        • Я и прошу перелогиниться в gost, так привычнее.
                                                          Ответить
                                                        • У госта аватарка неняшная, если это он, то пусть лучше эта файка будет.
                                                          Ответить
                                                • И сразу нету проблем с выравниванием, например
                                                  Ответить
                                                • Массив чаров, aka байтов.
                                                  Ответить
                                              • 256 типов + сигнатур функций. Указатели на функции же тоже можно хранить.
                                                Ответить
                                          • > Ну std::any это же и есть просто структурка из void* + type_info*. Куда компактнее, разве что на выравнивание забить и хранить тип в uint8_t или вообще в битах указателя?

                                            Ну так требования к выравненности указателей на разных платформах могут быть раные, и на какой-то платформе если указатель 64-битный но его допустимо выравнивать по 32-битной границе, и свой аналог type_info будем хранить в 32-битах, то массив из такой структуры будет по 32 бита экономить на каждый элемент, в сравнении с двумя обычными указателями
                                            Ответить
                                            • > указатель 64-битный но его допустимо выравнивать по 32-битной границе

                                              Ну это странная архитектура какая-то... Требует чтения не меньше 32 бит, но при этом кеша нет и от натурального выравнивания 64 бит на 64 профита нету?
                                              Ответить
                                              • В AVR указатель 16-битный, но обычные регистры - 8-битные. Никаких требований к выравниваниям нет. Не вижу проблемы.
                                                Ответить
                                          • Кстати, можно даже такое сделать
                                            struct
                                            {
                                              uint16_t my_typeinfo;
                                              uint8_t data[]; // а что тут за фигня и сколько в ней байтиков - фиг его знает, понять можно на основе my_typeinfo
                                            }


                                            и делать связный список из такой фигни
                                            Ответить
                                          • Можно сделать манагер мапяти, чтобы он тоже знад о типах, и чтобы у объектов каждого типа была своя область в памяти. Тагды проверка типа сводится к some_class_store_begin <= p && p < some_class_stroe_end.
                                            Ответить
                                            • Предлагаю использовать сегменты, чтоб проверка границ этих областей была хардварной
                                              Ответить
                                            • И стек нахуй, чтобы не портить проверку. Хранить только адреса возврата и указатели на аргументы.
                                              Ответить
                                              • Адреса возврата в стеке данных — самая дорогая ошибка проектирования в компьютерной истории. Billion-dollar mistake на этом фоне выглядит детским лепетом.
                                                Ответить
                                                • А как бы ты сделала?
                                                  Ответить
                                                  • Джва стека, няверное.
                                                    Ответить
                                                    • Для адресов разврата и для автоматических пельменных?

                                                      Интересно решение, кстати:)
                                                      Ответить
                                                  • Ня знаю, я простая аниме-девочка. Но простой двойной стек — для данных и для адресов возврата — уже будет гораздо лучше ужаса x86.
                                                    Ответить
                                                • > Адреса возврата в стеке данных — самая дорогая ошибка проектирования в компьютерной истории.

                                                  Когда никакого интернета не было, компьютеры занимали большой машинный зал и программировались через перфоленты-перфокарты, ошибкой это не было. Эксплоиты никого не парили, потому что кто-то просто приходит со своей программой, запускает ее, получает распечатку с результатом выполнения и потом уходит.
                                                  Ответить
                                                  • Но использование этой схемы в компьютерных системах, выполняющих недоверенный код и/или получающих недоверенные данные, является чудовищной ошибкой.
                                                    Ответить
                                                  • охблин, ну тогда и защиты памяти и портов поди никакой не было
                                                    Ответить
                              • > эксепшенами

                                Да, крестолиба к сожалению на них намертво завязана. Ты можешь их не юзать, конечно. Но приходится сразу прощаться с векторами и прочими няшными штуками.
                                Ответить
                                • Пушо в какой-то момент они казались очень удачной идеей.
                                  Джава вся тоже в исключениях
                                  Но уже в .NET стали завозить всякие "TryParse", то есть уже в 1999-м было понятно, что исключения это не всегда хорошо
                                  Ответить
                                  • В микроконтроллерах они никогда не казались удачной идеей. Поэтому я против эксепшенов.
                                    Ответить
                                    • накладные расходы потому что, и кстати вот исключениям уж точно нужен какой-то рантайм (да?)

                                      ничего, ща вам микропитон завезут в контроллеры, и будут у вас и исключения, и ооп, и всё, что ты любишь
                                      Ответить
                                      • > и кстати вот исключениям уж точно нужен какой-то рантайм (да?)

                                        Тут есть кое-какие ньюансы. Если исключения реализуются через механизм setjmp/longjmp хуйни, то от рантайма там только эта setjmp/longjmp хуита нужна, а это вообще из сишного рантайма хрень.

                                        А если механизм исключений реализуется через unwind tables какие-то, см
                                        https://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-dw2.c

                                        т.е. в бинарнике есть некое говно https://habr.com/ru/post/208006/ :

                                        > Концептуально, на каждый адрес кода программы хранится информация о том, как попасть в вышестоящий фрейм вызова. На практике ввиду объемности этой информации, она сжимается, фактически, вычисляется с помощью интерпретации байт-кода. Этот байт-код исполняется при возникновении исключения. Расположено всё это в секциях ".eh_frame" и ".eh_frame_hdr".
                                        Да, помимо всего прочего, DWARF интерпретатор представляет собой отличный backdoor, с помощью которого, подменив байт-код, можно перехватить исключение и отправить его на обработку куда душе угодно.

                                        Вот это уже серьезный рантайм.

                                        А в винде тоже есть свое говно с какими Thread Information Block, и о нем в той статье на хабре тоже упоминается.

                                        Еще можно какое-то говно через ретурны сделать теоретически, даже setjmp/longjmp не потребуется, но это пиздец будет, после каждого ретурна проверяем хуйню и если хуйня выставлена то тогда опять ретурним... можно какой-то говнопрепроцессор под это сделать. Считать ли такую дрисню рантаймом?
                                        Ответить
                                        • > Если исключения реализуются через механизм setjmp/longjmp хуйни, то от рантайма там только эта setjmp/longjmp хуита нужна, а это вообще из сишного рантайма хрень.

                                          Тут конечно же надо уточнить, что если надо просто прыгнуть по стекфрейам - это одно дело, а если надо прыгнуть по стекфреймам с вызовом деструкторов для всякого там RAII по дороге - это уже другое. И там уж в стекфрейме надо вспомогательную инфу хранить, типа если бросили исключения, то вот эти деструкторы надо б вызвать по дороге... А что если исключение произойдет в деструкторе когда мы уже обрабатываем исключение? Скорее всего, упадет всё нафиг.
                                          Ответить
                                          • Поэтому throwing деструкторы считаются очень большим харамом. Но если уж совсем хочется — можно проверить результат std::uncaught_exceptions(). Если он не равен нулю, то мы находимся в процессе раскрутки стека, и бросать новое исключение из деструктора — верная дорога в ад. А если активных исключений нет, то, в принципе, можно и бросить.
                                            Ответить
                                            • > Но если уж совсем хочется — можно проверить результат std::uncaught_exceptions(). Если он не равен нулю, то мы находимся в процессе раскрутки стека, и бросать новое исключение из деструктора — верная дорога в ад.

                                              Ну ок, допустим что есть raii-шная обертка над файловым дескриптором, которая в деструкторе делает close(fd). Но close может завершиться с ошибкой:
                                              RETURN VALUE
                                                     close() returns zero on success.  On  error,  -1  is  re‐
                                                     turned, and errno is set appropriately.
                                              
                                              ERRORS
                                                     EBADF  fd isn't a valid open file descriptor.
                                              
                                                     EINTR  The  close() call was interrupted by a signal; see
                                                            signal(7).
                                              
                                                     EIO    An I/O error occurred.
                                              
                                                     ENOSPC, EDQUOT
                                                            On NFS, these errors  are  not  normally  reported
                                                            against  the  first write which exceeds the avail‐
                                                            able storage space, but instead against  a  subse‐
                                                            quent write(2), fsync(2), or close().

                                              Предположим, где-то в коде мы успешно открыли файл, начали с ним что-то делать, и тут срабатывает исключение в какой-то фигне, мы раскручиваем стек, надо вызвать close() для файла, но вызвать close не выходит. Ну проверили мы std::uncaught_exceptions(), ну да, раскручиваем стек, и че? Какие дальнейшие действия?
                                              Ответить
                                              • > Какие дальнейшие действия?

                                                А в сишке?
                                                Ответить
                                                • Нннну например сохраняем неосвобождаемый файловый дескриптор в стек для неосвобождаемых файловых дескрипторов, и пробуем его освободить потом (может помочь с EINTR).

                                                  Или можно сделать exit(EXIT_FAILURE);

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

                                                    Это как? close() вроде нельзя звать второй раз, ошибка из него неисправима.

                                                    Ну т.е. два адекватных варианта остаётся:

                                                    - заигнорить (можно и в крестах)
                                                    - завершить прогу (кидай исключение, кресты сами завершат)
                                                    Ответить
                                                    • > Это как? close() вроде нельзя звать второй раз, ошибка из него неисправима.

                                                      Тут всё не так однозначно

                                                      https://stackoverflow.com/a/33114363

                                                      > In theory, POSIX was unclear in the past as to whether the fd remains open when close fails with EINTR, and systems disagreed. Since it's important to know the state (otherwise you have either fd leaks or double-close bugs which are extremely dangerous in multithreaded programs), the resolution to Austin Group issue #529 specified the behavior strictly for future versions of POSIX, that EINTR means the fd remains open. This is the right behavior consistent with the definition of EINTR elsewhere, but Linux refuses to accept it. (FWIW there's an easy workaround for this that's possible at the libc syscall wrapper level; see glibc PR #14627.) Fortunately it never arises in practice anyway.

                                                      > Per the (amended) standard, on EINTR the fd remains open. However, Linux does not honor this and glibc does not work around the failure to honor it. See the links in my answer. Fortunately EINTR does not happen on close on Linux in any real-world situations I'm aware of, anyway.
                                                      Ответить
                                                    • Собственно, вот
                                                      https://man7.org/linux/man-pages/man2/close.2.html
                                                      
                                                      
                                                             The EINTR error is a somewhat special case.  Regarding the EINTR
                                                             error, POSIX.1-2008 says:
                                                      
                                                                    If close() is interrupted by a signal that is to be
                                                                    caught, it shall return -1 with errno set to EINTR and the
                                                                    state of fildes is unspecified.
                                                      
                                                             This permits the behavior that occurs on Linux and many other
                                                             implementations, where, as with other errors that may be reported
                                                             by close(), the file descriptor is guaranteed to be closed.
                                                             However, it also permits another possibility: that the
                                                             implementation returns an EINTR error and keeps the file
                                                             descriptor open.  (According to its documentation, HP-UX's
                                                             close() does this.)  The caller must then once more use close()
                                                             to close the file descriptor, to avoid file descriptor leaks.
                                                             This divergence in implementation behaviors provides a difficult
                                                             hurdle for portable applications, since on many implementations,
                                                             close() must not be called again after an EINTR error, and on at
                                                             least one, close() must be called again.  There are plans to
                                                             address this conundrum for the next major release of the POSIX.1
                                                             standard.
                                                      Ответить
                                                      • > and the state of fildes is unspecified

                                                        Заебись. Т.е. если я пишу кроссплатформенный код, мне придётся писать ifdef'ы для каждой операционки, которая возвращает ошибку при EINTR? Ну и забивать на утечку дескрипторов для неизвестных мне ОС.

                                                        В очередной раз убеждаюсь, что нехуй вообще было мешать запись и закрытие в один сисколл. Лучше бы всегда в успешной ветке флашить чем пердолиться с ошибками от закрытия... Но теперь это говно уже не исправить, походу.
                                                        Ответить
                                                        • >В очередной раз убеждаюсь, что нехуй вообще было мешать запись и закрытие в один сисколл. Лучше бы всегда в успешной ветке флашить чем пердолиться с ошибками от закрытия... Но теперь это говно уже не исправить, походу.

                                                          Именно поэтому я за микроконтроллеры - там этой хуйни вообще нет изначально.
                                                          Ответить
                                                          • > там этой хуйни вообще нет изначально

                                                            В каких-нибудь либах от вендоров такого говнеца хватает, я думаю. Но их можно не юзать.
                                                            Ответить
                                                      • Ответить
                                                        • > // log & ignore
                                                          Надо еще учесть, что при логгировании может эксепшен сработать.
                                                          Ответить
                                                          • > Надо еще учесть, что при логгировании может эксепшен сработать.

                                                            Да, но тут лучше весь логгер noexcept'ным сделать раз и навсегда. В сишке ведь тоже никто не обрабатывает ошибки от логирующих макросов.
                                                            Ответить
                                                            • > Да, но тут лучше весь логгер noexcept'ным сделать раз и навсегда.
                                                              Так и надо, никогда не видел, чтобы у логгера были какие-то ошибки.
                                                              Проиницализировал и забыл.
                                                              Ответить
                                          • > А что если исключение произойдет в деструкторе когда мы уже обрабатываем исключение? Скорее всего, упадет всё нафиг.

                                            https://habr.com/ru/post/433944/ - да, упадет.
                                            Ответить
                                      • > накладные расходы

                                        Да кстати нет... Все вот эти if (err) return err весят не сильно меньше. Успешный путь исключения не портят (в нормальном конпеляторе). Да и по фейлу время вполне детерминированное.
                                        Ответить
                                        • > Да кстати нет... Все вот эти if (err) return err весят не сильно меньше.

                                          А если всю программу хуйнуть в одну функцию и скакать по ней через goto?
                                          Ответить
                                          • Так не честно )))

                                            Кстати, вот многие сишники плюются от исключений, а сами юзают убогий setjmp.
                                            Ответить
                                            • Почему убогий? Наоборот, серьёзный инструмент для суровых людей, чтобы прямо из сишки лезть в регистры без асмовставок.
                                              Ответить
                                          • А потом для компиляции разделять эту функцию на мелкие кусочки препроцессором на PHP. Примерно так крупные компании делают, например, Телеграм: https://github.com/tdlib/td/blob/master/SplitSource.php.

                                            Но если серьёзно, то похожий подход применяется в sqlite, например: там перед компиляцией все исходники сваливаются в один огромный .c-файл — пишут, что так оптимизация лучше получается на десяток процентов.
                                            Ответить
                                            • $td_methods = array(
                                                              'animations_manager[_(-][^.]|AnimationsManager[^;>]' => "AnimationsManager",
                                                              'audios_manager[_(-][^.]|AudiosManager' => "AudiosManager",
                                                              'auth_manager[_(-][^.]|AuthManager' => 'AuthManager',
                                                              'background_manager[_(-][^.]|BackgroundManager' => "BackgroundManager",
                                                              'ConfigShared|shared_config[(]' => 'ConfigShared',
                                                              'contacts_manager[_(-][^.]|ContactsManager([^ ;.]| [^*])' => 'ContactsManager',
                                                              'country_info_manager[_(-][^.]|CountryInfoManager' => 'CountryInfoManager',
                                                              'documents_manager[_(-][^.]|DocumentsManager' => "DocumentsManager",
                                                              'file_reference_manager[_(-][^.]|FileReferenceManager|file_references[)]' => 'FileReferenceManager',
                                                              'file_manager[_(-][^.]|FileManager([^ ;.]| [^*])|update_file[)]' => 'files/FileManager',
                                                              'G[(][)]|Global[^A-Za-z]' => 'Global',
                                                              'group_call_manager[_(-][^.]|GroupCallManager' => 'GroupCallManager',
                                                              'HashtagHints' => 'HashtagHints',
                                                              'inline_queries_manager[_(-][^.]|InlineQueriesManager' => 'InlineQueriesManager',
                                                              'language_pack_manager[_(-][^.]|LanguagePackManager' => 'LanguagePackManager',
                                                              'LogeventIdWithGeneration|add_log_event|delete_log_event|get_erase_log_event_promise|parse_time|store_time' => 'logevent/LogEventHelper',
                                                              'MessageCopyOptions' => 'MessageCopyOptions',
                                                              'messages_manager[_(-][^.]|MessagesManager' => 'MessagesManager',
                                              ф               'notification_manager[_(-][^.]|NotificationManager|notifications[)]' => 'NotificationManager',
                                                              'phone_number_manager[_(-][^.]|Pho

                                              днище-то какое
                                              Ответить
    • Да, в студии обработка шаблонов вообще странная. Они их походу даже не парсят толком до момента подстановки, поэтому пропущеные typename и даже опечатки не ловятся.
      Ответить
    • Crpt
      Ответить
    • А как это шаблоноговно подчищается? Ну допустим есть много разных единиц трансляции (.cpp), и в них инклудился тот же самый хедер с шаблонами(.hpp), и там встречаются аналогичные специализации шаблоноговна, из одинаковых специализаций шаблоноговна генерится одинаковая питушня (функции, классы с методами там), и потом линкер это говно должен вилкой чистить? А может быть можно мемоизацию какую-то сделать, ну типа если какой-то хедер много-куда инклудится и такой-то шаблон оттуда специализируется такой хуитой много где, то чтоб не плодить кучу одинаковой хуйни и потом ее к хуям убирать, оставляя лишь один экземпляр поебени специализированной по такой хуйне? Есть такое?
      Ответить
      • А для этого в крестах смысл "inline" и поменяли по сравнению с сишкой. Это как раз разрешение мёржить одноимённую фигню из разных единиц трансляции (comdat).

        Мемоизация тоже есть (prrecompiled headers), но она вроде только на парсинг, а не на кодогенерецию.
        Ответить
        • В VS вроде и на кодогенерацию шаблонов тоже, и из-за этого как раз они долгое время совсем не по стандарту парсились.
          Ответить
          • Да она и сейчас своеобразная.

            Помнится, я в студии умудрился внутри функции шаблонов наебашить. А потом удивился, что гцц со шлангом так не умеют.
            Ответить
        • Так.

          Причём шаблонные классы, шаблонные функции, методы, полностью определённые в определении класса, constexpr-функции, constexpr-статические члены класса и ещё какие-то сущности объявлены inline по умолчанию, и линкер обязан их чистить вилкой без явных указаний программиста.
          Ответить
          • > обязан их чистить вилкой

            Т.е. у инлайн функции во всех единицах трансляции один адрес? Хм, не задумывался о такой гарантии.
            Ответить
            • Ну да, инлайн функции/переменные же должны "указывать" на один и тот же объект во всех TU.
              // TU1.cpp
              inline int x;
              voif f1() { x = 2; }
              
              // TU2.cpp
              inline int x;
              void f2() { x *= 2; }
              
              // main.cpp
              inline int x;
              int main() { f1(); f2(); printf("%d", x); } // 4
              Ответить
              • Плюсовики изнасиловали ODR конечно. И процесс линковки -- тоже.

                А всё потому, что код смешали с данными
                Ответить
                • > код смешали с данными

                  При этом невиртуальные функции вообще никак не привязаны к данным после конпеляции.
                  Ответить
                  • ну да, по сути же это просто функции в удобном "неймспейсе", которые получают адрес какого-то говна неявно
                    Ответить
          • Лучше б изменили стандартную схему компиляции, когда тупо генерятся объектники на каждую единицу трансляции, и потом там еще линкеру надо что-то чистить. Можно проинстанцированные шаблоны набрасывать в какую-то БД и потом оттуда брать при повторном инстанциировании того же шаблона той же фигней в другой единице трансляции.
            Ответить
            • Слишком сложно получается. Сейчас это всё на уровне процессов параллелится и между ними вообще никакого взаимодействия нету. А тут получится IPC, база и т.п. Разве что тредами в одном процессе конпелять.
              Ответить
              • Можно сделать БД для компиляции, в которую вхуячиваются все исходные коды и либы всякие (зависимости) и в которую куча компиляторов (в т.ч. удаленно с разных компов) может читать всякую хрень оттуда (ну типа в таком .cpp файле такая хуйня написана, в таком .hpp файле такая хуйня, а вот тут какой-то другой компилятор уже проинстанцировал такой-то шаблон такими-то параметрами) и записывать туда хрень одновременно, а потом из этой БД как-нибудь экстрактить откомпилированный бинарь.

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

                  А так они срут одинаковым говном, которое потом мерджится
                  Ответить
                  • Генерить ее можно один раз из набора .cpp, .hpp файлов и всяких там объектников каких-то либ.

                    > А так они срут одинаковым говном, которое потом мерджится

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

                      Хотя в целом конечно идея не компилировать одно и тоже -- хорошая
                      Ответить
                      • > "Один раз" это перегенеривать после каждого изменения же, иначе как узнать что я там поменял?

                        Можно коммитить туда, как git какой-нибудь.
                        Ответить
                        • Ну да, норм идея. Заодно эту базу можно юзать для дополнения в IDE.
                          Ответить
                        • Напоминает задачу про построение списка зависимостей у сишеных файлов.

                          Раньше это делали вручную или внешней тулой (кажется у pmake есть команда или даже внешняя, makedepend), потом вроде gcc сам научился.
                          Ответить
      • есть два стула
        https://gcc.gnu.org/onlinedocs/gcc/Template-Instantiation.html
        Ответить

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