Хроника пикирующих граблей. C# и другие
Sep. 3rd, 2005 12:25 am(UPDATE log: с аргументом делегата - меня научили военным хитростям)
Нормальные люди, как известно, начинать считают с единицы, программисты - с нуля. BSD'шники переплюнули программистов: у них счёт начинается с минус единицы.
Первый пример: чтобы во FreeBSD в /etc/rc.conf задать адрес на сетевом интерфейсе, надо определить переменную ifconfig_ZZZ, где ZZZ - имя интерфейса. Если одного адреса не хватает... заводится ifconfig_ZZZ_alias0. Нормальный человек назвал бы её примерно как ifconfig_ZZZ_address2, программист - помня, что основной адрес вроде как нулевой - ifconfig_ZZZ_alias1. Видимо, радикализма программистов оказалось недостаточно.
Второй пример: логфайлы. Если /var/log/messages "проротейчен" (сдвинут в сторону и на его месте открыт новый) - он становится... да, уже многие догадались (если не знали уже) - /var/log/messages.0. (Ну или /var/log/messages.0.gz, если он ещё и сжимается.) Почему 0, почему не 1? Видно, великая тайна есть в развитии BSD систем, которую не понять даже такому привыкшему уже к FreeBSD старому извращенцу, как я.
К чему это я вспомнил? Ну да, читая книгу по C# обнаружил, что старый добрый printf авторов оного не устроил (ну ещё бы, великий принцип NIH - "not invented here" - руководит чуть ли не всей деятельностью конструкторской и программной разработки). Вместо старых добрых %<n>.<m><f> (например, %d - целое число со знаком, или %12.3f - вещественное число в формате фиксированной точки с 12 позициями всего и 3 после точки) - введено нечто принципиально новое: {номер_аргумента,ширина:формат}. Сразу вспомнился gettext: его выгибоны для перестановки параметров получили естественное подкрепление, теперь локализовать выводимые сообщения в System.String.Format() значительно легче. Но первый аргумент после форматной строки... имеет номер 0! Видимо, Microsoft вместе с сетевым стеком FreeBSD подхватило вирус "минус единицы", пронумеровав ею саму форматную строку.
То что совместимость с семейством printf потеряли - об этом можно и не вспоминать. Вперёд, заре навстречу, товарищи в борьбе... Да, аналогов подходов funopen() и fopencookie() в System.IO нету, не по чину такое реализовывать. Хорошо, что хоть не мешают это делать построением своих классов, в отличие от проприетарных stdio.
С тредами мы наблюдаем другой вид чудес. Для запуска нового треда в C# надо, кроме прочих шаманских действий, занести свой метод в делегат следующего вида:
Сравнивая с другими реализациями (например, pthreads) видим отсутствие аргумента на входе:
"Гады! Редиски! Куда аргумент дели?" Не надо думать, что этот плач бессмысленный; если бы передавался параметр типа object, можно было бы через него передать любую информацию, точно так же как через void* в plain C. А что остаётся - глобальные переменные? А если у нас два или больше, например, идентичных по схеме построения worker pool'а, но с разными множествами рабочих нитей (абсолютно нормальный дизайн, однако)? Сериализовать старт новых нитей? А ведь иного и не остаётся. Тут уже подсказали, что есть типа секретный метод (Шилдт про него молчал как партизан, надо было тщательно проштудировать примеры кода чтобы понять трюк), так что исправляюсь - не всё так безнадёжно. Но - 1) почему это не описывают явно и открыто? 2) почему так через одно место? На дворе всё-таки даже для первой версии был 2000-й год, а не 1970-й.
Это напомнило мне метод старта новых процессоров в Intel SMP (подробности на developer.intel.com в мануалах к Pentium, для более поздних процессоров эта документация не дублировалось). Передать что-то процессору в том IPI (inter-process interrupt), который его запускает, например, адрес точки старта и значение хотя бы одного регистра? Да боже упаси, от этого корона свалится! Поэтому:
- старт процессоров, ясен пень, сериализуется - пока один не отрапортовал что тангаж, крен и рысканье в норме, следующие на взлёт не отправляются.
- пишется системная процедурка, которая вылавливает из памяти из глобальной переменной, кого же стартуем, и начинает соответствующую отработку.
- в область данных BIOS пишется специальное указание "после проверки процессора переходить на заданный адрес" (через него в 80286, в частности, делался переход в real mode). Что будет, если в этот момент придёт reset - можно только догадываться, скорее всего, машину придётся дёргать только по питанию.
- наконец, толкаем IPI. Процессор выйдя из спячки уходит выполнять код по стандартным FFFF:0000 в real mode. Прочитав заветный приказ по 0x0470-0x0473, уходит на процедуру ОС. Стартовав, та снимает все эти навороты чтобы reset сработал:)
Стра-ашно? Вот-вот, теперь берём передачу в делегат вместе с методом объекта - объект и уходим от этого ужаса. Довольные тем, что перехитрили всех:)
Впрочем, first-class functions с возможностью конструирования их на ходу от этого всё равно не появятся. Какое-то подобие замыканий (и опять через двуединую сущность с вертикальной улыбкой!) обещают во второй версии, да когда же она будет достаточно распространена...
Впрочем, мне это всё пока что теория - ни одной живой строчки на нём я не написал и, видимо, не скоро напишу. Может, у реальных писателей на оном, стаж которых к данному моменту достиг уже 10 лет непрерывного применения в сложных условиях борьбы с тараканами оконного интерфейса и оптимизациями языка 1С, мои плачи вызовут только ироническую ухмылку - не тем, мол, занимаешься. Ладно, посмотрим.
А вообще, впечатление от оной продукции, мягко говоря, грустное. Что стоило, например, убрать вечную проблему "к какому if относится данный else?" и сделать нормальный else-if? Неужели сохранение совместимости с дизайном 35-летней давности (старше конструкции моего пердящего драндулета) стоит того, чтобы распространять грабельное поле ещё на ближайшие 10-20 лет?
Нормальные люди, как известно, начинать считают с единицы, программисты - с нуля. BSD'шники переплюнули программистов: у них счёт начинается с минус единицы.
Первый пример: чтобы во FreeBSD в /etc/rc.conf задать адрес на сетевом интерфейсе, надо определить переменную ifconfig_ZZZ, где ZZZ - имя интерфейса. Если одного адреса не хватает... заводится ifconfig_ZZZ_alias0. Нормальный человек назвал бы её примерно как ifconfig_ZZZ_address2, программист - помня, что основной адрес вроде как нулевой - ifconfig_ZZZ_alias1. Видимо, радикализма программистов оказалось недостаточно.
Второй пример: логфайлы. Если /var/log/messages "проротейчен" (сдвинут в сторону и на его месте открыт новый) - он становится... да, уже многие догадались (если не знали уже) - /var/log/messages.0. (Ну или /var/log/messages.0.gz, если он ещё и сжимается.) Почему 0, почему не 1? Видно, великая тайна есть в развитии BSD систем, которую не понять даже такому привыкшему уже к FreeBSD старому извращенцу, как я.
К чему это я вспомнил? Ну да, читая книгу по C# обнаружил, что старый добрый printf авторов оного не устроил (ну ещё бы, великий принцип NIH - "not invented here" - руководит чуть ли не всей деятельностью конструкторской и программной разработки). Вместо старых добрых %<n>.<m><f> (например, %d - целое число со знаком, или %12.3f - вещественное число в формате фиксированной точки с 12 позициями всего и 3 после точки) - введено нечто принципиально новое: {номер_аргумента,ширина:формат}. Сразу вспомнился gettext: его выгибоны для перестановки параметров получили естественное подкрепление, теперь локализовать выводимые сообщения в System.String.Format() значительно легче. Но первый аргумент после форматной строки... имеет номер 0! Видимо, Microsoft вместе с сетевым стеком FreeBSD подхватило вирус "минус единицы", пронумеровав ею саму форматную строку.
То что совместимость с семейством printf потеряли - об этом можно и не вспоминать. Вперёд, заре навстречу, товарищи в борьбе... Да, аналогов подходов funopen() и fopencookie() в System.IO нету, не по чину такое реализовывать. Хорошо, что хоть не мешают это делать построением своих классов, в отличие от проприетарных stdio.
С тредами мы наблюдаем другой вид чудес. Для запуска нового треда в C# надо, кроме прочих шаманских действий, занести свой метод в делегат следующего вида:
public delegate void ThreadStart();Сравнивая с другими реализациями (например, pthreads) видим отсутствие аргумента на входе:
typedef void *(*pthread_startroutine_t)(void *);"Гады! Редиски! Куда аргумент дели?" Не надо думать, что этот плач бессмысленный; если бы передавался параметр типа object, можно было бы через него передать любую информацию, точно так же как через void* в plain C. А что остаётся - глобальные переменные? А если у нас два или больше, например, идентичных по схеме построения worker pool'а, но с разными множествами рабочих нитей (абсолютно нормальный дизайн, однако)? Сериализовать старт новых нитей? А ведь иного и не остаётся. Тут уже подсказали, что есть типа секретный метод (Шилдт про него молчал как партизан, надо было тщательно проштудировать примеры кода чтобы понять трюк), так что исправляюсь - не всё так безнадёжно. Но - 1) почему это не описывают явно и открыто? 2) почему так через одно место? На дворе всё-таки даже для первой версии был 2000-й год, а не 1970-й.
Это напомнило мне метод старта новых процессоров в Intel SMP (подробности на developer.intel.com в мануалах к Pentium, для более поздних процессоров эта документация не дублировалось). Передать что-то процессору в том IPI (inter-process interrupt), который его запускает, например, адрес точки старта и значение хотя бы одного регистра? Да боже упаси, от этого корона свалится! Поэтому:
- старт процессоров, ясен пень, сериализуется - пока один не отрапортовал что тангаж, крен и рысканье в норме, следующие на взлёт не отправляются.
- пишется системная процедурка, которая вылавливает из памяти из глобальной переменной, кого же стартуем, и начинает соответствующую отработку.
- в область данных BIOS пишется специальное указание "после проверки процессора переходить на заданный адрес" (через него в 80286, в частности, делался переход в real mode). Что будет, если в этот момент придёт reset - можно только догадываться, скорее всего, машину придётся дёргать только по питанию.
- наконец, толкаем IPI. Процессор выйдя из спячки уходит выполнять код по стандартным FFFF:0000 в real mode. Прочитав заветный приказ по 0x0470-0x0473, уходит на процедуру ОС. Стартовав, та снимает все эти навороты чтобы reset сработал:)
Стра-ашно? Вот-вот, теперь берём передачу в делегат вместе с методом объекта - объект и уходим от этого ужаса. Довольные тем, что перехитрили всех:)
Впрочем, first-class functions с возможностью конструирования их на ходу от этого всё равно не появятся. Какое-то подобие замыканий (и опять через двуединую сущность с вертикальной улыбкой!) обещают во второй версии, да когда же она будет достаточно распространена...
Впрочем, мне это всё пока что теория - ни одной живой строчки на нём я не написал и, видимо, не скоро напишу. Может, у реальных писателей на оном, стаж которых к данному моменту достиг уже 10 лет непрерывного применения в сложных условиях борьбы с тараканами оконного интерфейса и оптимизациями языка 1С, мои плачи вызовут только ироническую ухмылку - не тем, мол, занимаешься. Ладно, посмотрим.
А вообще, впечатление от оной продукции, мягко говоря, грустное. Что стоило, например, убрать вечную проблему "к какому if относится данный else?" и сделать нормальный else-if? Неужели сохранение совместимости с дизайном 35-летней давности (старше конструкции моего пердящего драндулета) стоит того, чтобы распространять грабельное поле ещё на ближайшие 10-20 лет?
no subject
Date: 2005-09-03 07:59 am (UTC)no subject
Date: 2005-09-03 08:42 am (UTC)no subject
Date: 2005-09-03 10:18 am (UTC)а) их (работ) всего четыре;
б) максимально возможный вычет составляет 2 балла по десятибалльной шкале.