вторник, 19 июня 2012 г.

[prog.c] Презентация Deep C (and C++)

Случайно наткнулся на блог норвежского разработчика Olve Maudal, а в нем на презентацию Deep C (and C++).

Интересно, очень познавательно.

Хотя лично я предпочту работать с людьми, которые не имеют столь глубоких знаний в C/C++ и, поэтому, будут писать так, чтобы только из кода было понятно, что и как работает. Как только в дело вступает глубокое знание тонких деталей стандарта или (что хуже) особенностей конкретных сред/компиляторов, тут нужно начинать бить по рукам. Если только не приходится каждый бит/такт экономить.

12 комментариев:

  1. Недавно пришлось использовать GCCизм. Правда его использование я могу оправдать тем, что работа библиотеки по принципиальным причинам пока возможна только в среде Linux (или окружении GNU с поддержкой соответствующих syscall-ов). Однако весьма и весьма удобный. Жаль, что нет в стандарте C.

    ОтветитьУдалить
  2. @buffovich:

    Ваня, так ты бы подробнее рассказал. Наверняка не только мне будет интересно.

    ОтветитьУдалить
  3. __attribute__ (( constructor ))
    __attribute__ (( destructor ))

    Святотатство может сказать кто-то... Будет отчасти прав. Я сейчас задам контекст. Упомянутая библиотека - динамически загружаемый модуль для MySQL. Если кто-то знаком с вопросом, то, наверное, знает, что MySQL при загрузке модуля сам не производит никакой его инициализации. В переносимых библиотеках подобная инициализация выполняется обычно явно пользователем с помощью "красной пилюли" init_, или _init. Однако это не тот случай. Вариантов поведения в указанном случае на самом деле не много - либо Lazy init в начале каждого экспортируемого вызова (ах да, забыл сказать, что у нас система ну пипец какая многопроцессорная, а у MySQL ну просто до неприличного много нативных потоков, поэтому в этом месте задумываемся над мьютексами), либо использовать какие-нибудь трики самого динамического загрузчика-интерпретатора (ld.so обычно). ld.so в купе с рантаймом crtbegin.o и сrtend.o обеспечивают полную поддержку формата ELF. Первый вариант из триков - использование магических точек входа void _init( void ) и void _fini( void ) для инициализации и финализации библиотеки соответственно. Хотя имена этих точек могут быть заданы пользователем в опциях линкера на стадии сборки. Указанные функции будут находится в секциях .init и .fini файла. Всё отлично, никаких мьютексов, полная потокобезопасность. Однако. Указанная возможность объявлена нерекомендуемой, т.к. у нас может получится перетереть процедуру инициализации рантайма, что приведет к большой головной боли в будущем. Второй вариант из триков - использование секций .ctors и .dtors. Эти секции обрабатываются как раз кодом рантайма и служат специально для того, чтобы вызывать конструкторы глобальных объектов (да, в С++). Указанные GCCизмы позволяют использовать такую возможность и для C. Семантика вызова достаточно проста - void myLocalInit( void ). Я делаю в каждой единице компиляции (а конкретно в её заголовочном файле) помимо всего прочего в том числе extern-объявления её конструктора и деструктора. Это очень удобно, т. к. у нас нет глобального реестра инициализаторов, который нужно править, а во вторых, простым grep-ом вычисляются модули (единицы компиляции), которые делают какую-то работу при загрузке и выгрузке плагина.

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

    Ну вот как-то так.

    ОтветитьУдалить
  4. @buffovich:

    Ситуация понятна.

    А "счетчик Шварца" здесь не работает?

    ОтветитьУдалить
  5. Женя, извини, но не могу проассоциировать C и паттерн "счётчик Шварца". Кроме того, глобальных межмодульных переменных у меня нет и использование модулями друг друга в момент инициализации запрещено большой вывеской в файле с описанием экспортируемых функций. Кроме того - там всё равно будут мьютексы на изменение счётчика. Для функций вызываемых на выборках в 1 000 000 записей проверка мьютекса - это катастрофа.

    ОтветитьУдалить
  6. @buffovich:

    Наружу so-шка может выставлять только C-шный интерфейс, унутрях же вполне можно C++ные плюшки использовать.

    Счетчик Шварца использует стандартный механизм инициализации статических переменных C++ для однократного запуска кода инициализации/деинициализации объекта. И мьютексы там, насколько я себе это представляю, не нужны.

    ОтветитьУдалить
  7. Да, я согласен. Если бы можно было C++, то ничто нам не мешало бы использовать инициализаторы глобальных инстанций и уйти от указанного выше дела.

    ОтветитьУдалить
  8. Мьютексы нужны при Lazy init. Да, извини, не совсем понял твою идею.

    ОтветитьУдалить
  9. @buffovich:

    Ваня, просто ради любопытства: если у вас обычный Linux и обычный GCC, то что препятствует использованию C++? Или же это административный запрет заказчика?

    ОтветитьУдалить
  10. > Хотя лично я предпочту работать с людьми, которые не имеют столь глубоких знаний в C/C++ и, поэтому, будут писать так, чтобы только из кода было понятно, что и как работает.

    Я не пишу на c/c++ уже пару лет, но не увидел в презентации ничего что бы не нужно было знать.
    * что такое неявная декларация
    * как static влияет на видимость статической переменной за пределами модуля
    * как инициализируется статическая переменная
    * начальные значение автоматических переменных
    * порядок выполнения операций
    * упаковка структур, выравнивание

    и т.д. и т.п.

    ОтветитьУдалить
  11. @fuxx:

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

    static int a;
    ++a;
    std::cout << a << std::endl;

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

    Мне же спокойнее, когда человек сомневается в своих знаниях и из-за этого пишет

    static int a = 0;

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

    ОтветитьУдалить
  12. @Евгений Охотников пишет...

    я бы тоже предпочел читать

    static int a = 0;

    это еще и к копипасту... ой, я хотел сказать к рефакторингу устойчивее, чем

    static int a;

    а если речь вести о языке в целом, то подход D -- а именно инициализировать все, и требовать спец. синтаксис для отстутствия инициализации -- емнип,

    int a = void;

    -- выглядит лучше

    ОтветитьУдалить