1. Си / Говнокод #12653

    +135

    1. 1
    2. 2
    3. 3
    4. 4
    5. 5
    6. 6
    7. 7
    8. 8
    9. 9
    // где-то в коде нашлось
    PRIVATE IdxArray* idx_array_append_val_dyn(IdxArray* arr, PlmIndex idx)
    
    // private.h
    #ifdef PLM_TEST
    #define PRIVATE extern
    #else
    #define PRIVATE static
    #endif

    внезапно...

    Запостил: Try, 26 Февраля 2013

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

    • Ну на время юнит-тестирования делают не статиком, чтобы тест мог повызывать этот метод. А в продакшене он прячется внутри своего модуля. В чем тут говно, Try?
      Ответить
      • разве что в неудачном названии макроса. хотя хз
        Ответить
      • Не понятно почему static меняется на extern. По-моему, просто надо убирать префикс, не?
        Ответить
        • А вот хбз. Для функций extern не нужен. Если бы его юзали еще перед переменными - extern в модуле с реализацией был бы странным, его стоило бы вынести в ашку, но вот пихать этот PRIVATE в ашку тоже не очень логично...

          Ну вот разве что такое извращение:
          // в сишке с реализацией
          PRIVATE int some_var; // превратится в extern если тестируем
          
          // в сишке с тестом
          int some_var = 42;
          Но здесь мы теряем возможность проинициализировать переменную в сишке с реализацией.
          Ответить
      • >Ну на время юнит-тестирования делают не статиком, чтобы тест мог повызывать этот метод. А в продакшене он прячется внутри своего модуля.
        Это чтобы увеличить шанс получить UB из-за нарушения ODR? Прохладная стори.
        Ответить
        • Хоть нарушение ODR это UB, но все адекватные линкеры вполне видят его нарушения, и рассказывают о них юзеру... Ну кто в здравом уме запилит линкер, успешно собирающий код, в котором нет нужной функции или же выбирающий случайную версию функции, если их 2+? Если линкер так себя ведет - его место на помойке.
          Ответить
          • P.S. За no diagnostic required крестостандартизаторам надо линейкой по рукам яйцам.
            P.P.S. А в стандарте сишки вообще такого правила походу нет.
            Ответить
            • Я тебе так скажу. Обнаружить расхождение нарушения ОДР не всегда возможно. Ну вот смотрит линкер на функцию с одинаковой сигнатурой. В одной функции указывается в функции static переменная, а в другой с такой же сигнатурой она уже не стасик. Линкер просто не в состоянии сравнить каждую мелочь. Он конечно самые важные моменты запихивает в сигнатуру, но не более и поэтому большинство нарушений обнаружено не будет. Да и если поразмыслить он просто физически проанализировать досканально всю функцию не сможет. У него просто нет на это времени. Ему нужно проект собрать побыстрее, а не греть процессор сутками, сравнивая каждую мелочь. Стандартизаторы это требование потому и убрали, чтобы не пугать мелких производителей компиляторов сложностью крестов. Работа не окупается. Но если стиснуть зубы и быть достаточно дисциплинированным, то эта проблема уходит. Я такие штуки за километр чую, так как уже не в меру упорот. Я вангую, если в новом стандарте правильно запилют модули, то проблема ОДР останется лишь в воспоминаниях и в старом коде с хедерами. Но как знать. Время покажет.
              Ответить
              • > В одной функции указывается в функции static переменная, а в другой с такой же сигнатурой она уже не стасик.
                Ну для не инлайн функций никаких проблем с диагностикой быть не может. Неважно что там конфликтует в телах функций, линкеру достаточно сказать, что он видит две функции с одним именем (в случае крестов - с одинаковой сигнатурой).

                В С89, емнип, инлайнов вообще нет, поэтому и проблем нет, обсуждать можно только конкретные компиляторы.

                С99 статик инлайн никаких проблем вызвать не может, потому что он статик и наружу не торчит.

                Остаются крестоинлайны и не статик инлайны в С99. В крестах диагностику выдать очень сложно - линкеру пришлось бы сравнивать тела функций. В с99 extern inline может быть только один, но может не совпасть с inline версией в текущей единице сборки, а какую из них выберет компилятор - никто кроме него не знает (кстати походу именно по этой причине в не static inline с99 запрещает static переменные).

                Отсюда вывод: единственный случай, когда мы в полной жопе и диагностику запилить очень сложно - если не статическая инлайн функция имеет разное тело в разных сборочных единицах. Но тот, кто так пишет - ССЗБ.

                P.S. Я не думаю, что PRIVATE из топика стали бы привинчивать к инлайнам, поэтому там все норм.
                Ответить
                • Да любой #ifdef может нарушить ОДР. Многие люди, а в особенности с шаблонами, реализацию функции пишут прямо в классе, а это уже типа инлайн для метода и уже облегчает нарушение одр от модуля к модулю.

                  Но! Нарушить одр можно и от класса к классу (а не только в функциях). Так что мест для нарушений хоть залейся.

                  Вон по новому стандарту наличие private в классе или его отсутствие (там подругому сформулировано, но в контексте обсуждения не важно) меняет результат std::is_standart_layout.

                  Это к вопросу о #define private public в модулях, подключаемых для unit testов, чтобы протестировать приватные члены (и такое взбредает в голову некоторым).

                  За счет нарушения ОДР класса у нас сьезжает какая-нибудь функция из-за разного значения std::is_standart_layout в разных модулях в при использовании в этой функции. Нарушения одр вызывают целые цепочки нарушений одр (реальное УБ).

                  Это тут нам ещё повезло. Некоторыми компиляторами обнаруживается, но если нет, то гораздо хуже вылезают порчи памяти из-за нарушения одр в классе по одному из членов, например в одном из модулей в классе члена нет или он друго го типа.
                  Ответить
                  • > Да любой #ifdef может нарушить ОДР.
                    > например в одном из модулей в классе члена нет или он другого типа.
                    > порчи памяти из-за нарушения одр в классе по одному из членов
                    > Нарушить одр можно и от класса к классу...
                    ...запилив два одноименных класса в разных сборочных единицах, либо из-за рассогласования дефайнов/прагм между сборочными единицами. В обоих случаях - ССЗБ. Хотя случайно вполне можно нарваться, не спорю.

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

                    P.S. Друг ловил подобную проблему с ODR - сгенерил кутикреаторским мастером заготовку для плагина, а ашки-заглушки с пустыми классами удалить забыл. В итоге плагин собрался с пустым классом (т.к. инклудил он через "" а не через <>), а в сошке был полноценный. new в плагине выделяло намного меньше памяти, конструктор вызывался из сошки, последствия можно себе представить.
                    P.P.S. Проблему локализовали valgrind'ом, т.к. про лишнюю ашку никто даже не подумал.
                    Ответить
                    • > проблемы бы не было*
                      *Не было бы проблемы с рассогласованием раскладки полей класса и списка его методов. Проблема с телами методов осталась бы, т.к. адекватного способа их сравнения нет.
                      Ответить
                      • нет, небыло
                        ушло бы такое понятие, как почти одинаковый инлайн метод, так как ушло бы такое понятие как дубликат инлайн метода (или класса) из-за отсутствия хедеров в новых проектах.
                        Ответить
                    • А чем отличается "" от <> ?
                      Тем, что инклуд <> - это для файлов с шаблонами?
                      Ответить
                      • "//kokoko//kokoProject.h"
                        <kokoProject.h>
                        \\- Вроде по стандарту можно только так и так.
                        
                        \\Но не так:
                        "\\kokoko\\kokoProject.h"
                        "\kokoko\kokoProject.h"
                        "/kokoko/kokoProject.h"
                        <\kokoko\kokoProject.h>
                        </kokoko/kokoProject.h>
                        Но ждем тех, кто стандарт чтил.

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

                        Ну и я всегда считал, что <kokoProject.h> для случаев если путь указан в мейкфайле\файле проекта, а "//kokoko//kokoProject.h", если путь указан прямо в коде абсолютный или относительно того, что указан в мейкфайле\файле проекта.

                        Вообщем я не знаю.
                        Ответить
                      • В сишном стандарте красиво отмазались - <> ищет только в implementation defined месте, а "" ищет в implementation defined месте, а потом там же где <>. Крестовый не смотрел, т.к. лень, и не думаю, что там что-то более точное.

                        В gcc <> ищет в директориях, указанных в -I, затем в системных, а "" ищет относительно текущего файла, а затем там же где и <>.

                        В вижуалке, емнип, точно так же, но "" после директории текущего файла смотрит еще и в директорию того файла, который его заинклудил и так далее до самого верха, и только затем переходит к /I.

                        Краткое резюме - в 99% случаев ашки из своего проекта подключают через "", а из внешних библиотек через <>.
                        Ответить
                        • >а из внешних библиотек через <>.
                          А boost внешняя, но "boost\\smart_ptr\\shared_ptr.hpp"
                          Ответить
                          • пропиши extra include path
                            Ответить
                            • ты смог декодировать его сообщение?
                              #respect
                              я вообще не понял, что он хотел сказать
                              Ответить
                              • а я не понял, что здесь не понятно
                                #respect за #respect
                                p.s. а вообще, может я и @LispGovno одно лицо
                                Ответить
                                • "буст внешняя, но работает и так" - разница лишь в последовательности поиска
                                  "буст внешняя, но у меня работает только так" - засунул буст как подпапку своего проекта?
                                  "буст внешняя, но в сорцах буста используется вот так" - пиздёж, там прямые слеши, но в инклюдах собственного же говна "" использовать правильно
                                  "буст внешняя, но я вовсе не это хотел сказать" - ближе всего
                                  Ответить
                                  • "буст внешняя, а я ЛиспГовно"
                                    Ответить
                                    • >"буст внешняя, а я ЛиспГовно"
                                      кто тут ещё в кандидаты на LispGovno крайний?
                                      Ответить
                                    • Песчаный карьер - два человека...
                                      ...
                                      А на ликеро-водочный нет?.
                                      Ответить
                                    • На словах ты хуй простой, а на деле LispGovno.
                                      Ответить
                          • > А boost внешняя, но "boost\\smart_ptr\\shared_ptr.hpp"
                            Ну я же написал: "" ищет в implementation defined месте, а потом там же где <>. Т.е. все, что нашло бы <> найдет и "".

                            P.S. Нахуя писать \\ вместо /? Смотрится уёбищно, а все компиляторы вроде бы / понимают.
                            P.P.S. Да и, если мне изменяет память (в данном случае не уверен), по стандарту экраны в инклудах не пашут.
                            Ответить
                            • >P.S. Нахуя писать \\ вместо /? Смотрится уёбищно, а все компиляторы вроде бы / понимают.

                              виндузятники, сэр
                              P.S. фф предлагает заменить "виндузятники"на взяточники
                              Ответить
                              • Кстати, попробовал скормить gcc "test\\test.h". Ищет не test.h в папке test, а файл с именем test\\test.h в текущей. И, имхо, все правильно делает. Нечего поддерживать виндоблядские соглашения, если во всех компиляторах (включая вижуалку) работает /.
                                Ответить
                                • Ну а что ты хотел, если это виндослеши? В линуске оно не робает.
                                  Ответить
                              • >P.S. фф предлагает заменить "виндузятники"на взяточники
                                Это плагин Навального шалит. Жулики и воры.
                                Ответить
                          • Кресто98стандарт: If either of the characters ’ or \ or either of the character sequences /* or // appears in a q-char-sequence or a h-char-sequence, or the character " appears in a h-char-sequence, the behavior is undefined.

                            Сишка99: [i]f the characters ', \, ", //, or /* occur in the sequence between the < and > delimiters, the behavior is undefined. Similarly, if the characters ', \ , //, or /* occur in the sequence between the " delimiters, the behavior is undefined.

                            Так что \\ мало того, что уёищно смотрится, так оно еще и UB ;)

                            Юзай портабельный* / и будет тебе счастье.
                            *в стандарте не описано, как задавать путь к подпапкам, но / работает во всех известных мне компиляторах.
                            Ответить
    • я с таким сам недавно экспериментировал
      либа, состоящая из нескольких .h + .c файлов
      для собственной отладки можно было и как есть
      но т.к. надо было отдавать сторонней организации в хитрожопом объектном + бинарном виде, в целях сокрытия внутренней реализации переделал под static + инклюдил .c файлы напрямую в центральный .c (экспортируя наружу только несколько имен)

      к слову, второй вариант всё равно не прокатил, т.к. получающийся с помощью убогого gcc 2.7.х объектник .o, как бы я не изголялся, содержал все внутренние элементы, доступные поименно (они просто были помечены, как local - "t", но тем не менее, это было самое настоящее палево)
      так что пришлось все стасики (функции и константные массивы) вручную переинлайнить в местах вызова и получить отличный дуршлаг с вермишелью
      Ответить
      • если тебе на самом деле надо было .о отдавать, то да без шансов - .о есть промежуточный результат. (хотя в общем случае взаимозаменяемый со статической библиотекой. там чуть больше возможностей.)

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

        ЗЫ gcc 2.7? в музее работаешь? :)
        Ответить
        • > 2.7
          я не виноват
          такой вот у стороннего партнера оказался особенный тулчейн для их производственной линии, и ничего с этим было не поделать
          Ответить

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