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

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

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

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

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

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

TheIvan комментирует...

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

eao197 комментирует...

@buffovich:

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

TheIvan комментирует...

__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змы не мешают, а скорее наоборот.

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

eao197 комментирует...

@buffovich:

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

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

TheIvan комментирует...

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

eao197 комментирует...

@buffovich:

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

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

TheIvan комментирует...

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

TheIvan комментирует...

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

eao197 комментирует...

@buffovich:

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

fukanchik комментирует...

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

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

и т.д. и т.п.

eao197 комментирует...

@fuxx:

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

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

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

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

static int a = 0;

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

имя комментирует...

@Евгений Охотников пишет...

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

static int a = 0;

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

static int a;

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

int a = void;

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