Pull to refresh
102
0.9
Send message
Класс кольцевого буфера, причем без динамического выделения памяти (в контроллерах это зачастую моветон). То есть он садится на статически выделенный массив и реализует абстракцию кольцевого буфера над ним. Специализации для unsigned char и для int. И там и там возврат значения идет в регистре процессора. Собственно, это та часть за которую мы любим C++. Если смотреть код на ассемблере, то можно (закрыв глаза на некоторые признаки) думать, что просто над массивом прямо в коде реализовали кольцевой буфер. Я, в принципе, понимаю что если делать специализацию для сложной структуры, в которой будут свои ссылки на дочерние объекты, и т.д. — это становится нетривиальным. "… а вы так не делайте!" © Анекдот
Объявить интерфейс InputProcessor с чистой виртуальной функцией process_chunk() (и стандартными функциями лайф-цикл менеджмета — init, shutdown, export_stats). Реализовать несколько классов, реализующих этот интерфейс с реализацией единственного алгоритма подсчета в классе. Main разбирает опции командной строки, создает список обработчиков ввода (в зависимости от того, что запрошено), читает порции байт из входного файла и по-очереди кормит их обработчикам. В конце спрашивает каждый обработчик, что он насчитал. Мне кажется, что это более-менее стандартное решение для таких задач. Обращу внимание, что решение со списком обработчиков прекрасно параллелится при необходимости.

Но еще бы сделал два замечания. Во-первых, нужно оценить сложность обработчиков. Накладные расходы на обработку чанка будут как минимум — выбор адреса виртуальной функции и indirect call + ret. Если несколько обработчиков реализуют тривиальные алгоритмы (простое увеличение счетчиков) — их есть смысл запихнуть в один (несколько счетчиков подряд увеличить может быть дешевле чем сделать несколько indirect-call'ов плюс возможные негативные эффекты от нелокальности в кэше и пайплайне процессора, зависящие от продвинутости такового).

Во-вторых, следовало бы добавить InputDecoder. Потому что на вход может идти старый добрый ASCII, wide chars, unicode utf-8, может быть что-то еще. InputDecoder приводит все возможные варианты входа к единому внутреннему представлению. Для задачи wc — достаточно только определять класс символа (word-delimiter, line-delimiter, digit, printable, non-printable, composite). Для другой задачи — может быть перевести все в UNICODE (как в Java) и обработчики ввода писать только для UNICODE-чанков.

А-а, да — ни препроцессор ни шаблоны мне лично тут не нужны. От слова «совсем».
Ну вот тут мне повезло, видимо! :-) У нас системы либо относительно большие, но не требовательные к производительности (управление складом) — либо требовательные, но маленькие (приблуды для конвейера, например). Для больших и требовательных к производительности систем у меня хорошего решения нет. Если деньги позволяют — предложил бы нанимать лучших программистов, давать им современный C++ со всеми плюшками, и надеяться на лучшее… Но даже тут без гарантий…
Я же говорю: для больших проектов у нас есть Java. Похуже производительность (но с современными JIT — приемлемо), приколы с equals — но зато отсутствие явного управления памятью и отсутствие адресной арифметики сильно понижают порог вхождения. Там где на Java молодой боец уже будет вовсю давать годный продукт — он же на C/CPP будет иметь segmentation fault — core dumped и изучать выдачу valgrind в рабочее время. :-)

И я не говорил, что мы не обновляем компилятор. Наш стандарт с 2008 года Debian Stable с тем компилятором, который там есть. Моя изначальная мысль была, напомню, что при старом компиляторе странное поведение программы было почти 100% результатом ошибки программиста (как правило, в управлении памятью). Сейчас для очистки совести стоит посмотреть, что выдает gcc -S. Язык стал сложный, кодогенерация стала сложной, рамки в которых компиляторам допустимо интерпретировать намерения программиста стали сложными… И меня тут не сильно волнует, кто виноват — дизайнеры языка, авторы компилятора или программист. Очевидно, что программист средней квалификации не в состоянии прочитать и держать в памяти текущий стандарт C++ со всеми его тонкостями. Но работать и решать задачи при этом все-равно нужно. Вывод gcc -S позволяет понять, как именно компилятор понял ваши намерения. Дальше можно искать в стандарте, почему он это сделал и какими словами его направить в более правильную сторону.
Это философский вопрос. Альтернативная точка зрения была в том, что C был (и остается) успешным — потому что это очень простой и логичный язык. В нем есть свои исторические неудобства типа символьных массивов вместо строк. Но достоинства языка перевешивали. С++ в ранних редакциях решил многие проблемы C. Но потом народу понравилось, и в спецификацию языка начали включать все больше новых и интересных вещей. Мое личное мнение — bloat будет расти экспоненциально. Потому что язык становится сложной системой, где каждая новая возможность будет порождать непредусмотренные side-эффекты. Чтобы их сгладить или устранить — спецификация будет еще расширяться и усложняться. Опять же, мое мнение — что добром это не кончится. К большому счастью, C++ старается сохранять совместимость с предыдущими версиями стандарта (и даже с C, где это возможно). Это дает возможность выбрать разумное подмножество языка (которое сложно чем-то испортить) — и на нем жить без особых проблем.
Смысл? Для write-once, run everywhere уже есть Java и наработки на ней. Для «быстро и близко к железу» — C/selected CPP subset. Два-с-половиной этих языка закрывают 100% наших потребностей. Ну то есть нет — для всяких одноразовых штук еще есть awk и sed, конечно. :-)

Или это не мне было отвечено?
Не подменяте понятия, пожалуйста. Я знаю о недостатках препроцессора, и строю деятельность таким образом, чтобы эти недостатки не выливались в проблемы в коде. Должен признаться, что особых неудобств это не вызывает. Скажем, nul-terminated строки в C вызывают гораздо больше раздражения. Или, скажем, отсутствие возможности перегрузки оператора == в Java. Если у кого-то препроцессор на первом месте в списке раздражающих факторов — ну, удивительно для меня конечно — но путь у каждого свой…
Ну вот, начинаются compiler-specific keywords… :-( Некрасиво это по-моему. С дефайном пока работает так как нужно во всех известных мне компиляторах начиная с BCC 3.1. Ну а если вдруг где-то не сработает оптимизация константных выражений во время компиляции — мне как программисту, не трудно: поставлю в #define предвычисленное значение.

Ну и уж тогда к трюку с printf еще бы добавить наличие predefined-определений __FILE__ и __LINE__. Получается автоматическая идентификация точки печати. Что в Java, так её разэтак, можно сделать только в рантайме через раскрутку стека, и это может быть запрещено полиси java-машины, и руководство предупреждает нас, что это может быть дорогая операция. А в C/CPP — бесплатно и на этапе компиляции!
При всем уважении!.. Я пользуюсь препроцессором C, ну где-то с 90-91 года. Уж тридцать лет скоро! Но не помню я каких-то проблем, которые бы это вызывало. И у знакомых тоже нет. Разумеется, если писать сложные выражения где аргумент макроса используется несколько раз — а потом увлекаться операциями с побочными эффектами в аргументах, может быть плохо. "… а вы так не делайте!" © Анекдот
Ну да, шаблонный класс (например, кольцевого буфера). Включается в пару cpp-файлов. Соответственно, создается две специализации шаблона и два .o-файла. Мы не видим, чем бы мы за это платили. Это просто альтернатива тому, что мы бы сделали копию файла ringbuf.cpp и поиском-заменой поменяли указатели на хранимый тип. В данной ситуации шаблон — это опять упрощение и избавление от дублирования кода. При этом никаких выведений типов аргументов, никаких SFINAE — ничего! Компилируется махом. Понимается любым программистом средней квалификации, чего еще нужно?

Продолжая аналогию с автомобилем — от того, что мне дали спортивный автомобиль, мне что теперь дрифтовать и гонять 180 по городу? Ну есть любители, да… А наша цель — скучно и предсказуемо добраться из точки «А» в точку «Б» максимально экономным способом. И да, многие возможности, заложенные конструкторами в машину за те же деньги (бесплатно) при этом остаются не использованными. Ну так это проблема конструкторов, а не моя…

С другой стороны, я видел открытые проекты с последними новинками C++. Для компиляции «с нуля» в пору ставить ферму. Потому что все эти фокусы с шаблонами память жрут как не в себя и время компиляции растет просто удивительно! Но у людей свой путь — надеюсь, они видят в нем какие-то выгоды, которые бы это все оправдывали.
У каждого свой взгляд на один и тот же предмет. Та же текстовая подстановка — это начиная от search/replace в редакторе, через sed и до регулярных выражений (которые теперь есть вообще везде). Может быть она тупорылая — но простая, интуитивно понятная, и (eсли не начинать с ее помощью делать слишком умные вещи) весьма надежная. О вкусах спорить не буду. Радует вас что кто-то уходит с языка, ну что ж, в карантин любой повод для радости — ценность. Я радуюсь тому, что в C++ (несмотря на все усилия по его развитию) всегда кто-то остается! :)
Ба! Это вы еще не видели как препроцессор m4 делает sendmail.cf из sendmail.mc — вот где жесть-то! Препроцессор c/c++ — это просто зайка! :-)

А если без шуток, то опять-таки понятны ограничения #define — ну так и не нужно пытаться делать с ним clever tricks.

#deinfe IR_PIN_HIGH ((PIND & (1<<4))!=0)
...
if(IR_PIN_HIGH) { foobar(); }


Читаемость лучше? Однозначно! Дублирования кода меньше? Меньше! Почему это не применять? Ну да, понятно что можно сделать inline-функцию для того же. Но inline — это же пожелание, а не обязанность компилятора. А в контроллерах бывают места, где надо что-то проверить или дернуть биты в регистрах за определенное число тактов. Что касается этого define — компилятору трудно что-то неправильное с ним сделать.

Вторая супер-способность #define — это обращать куски кода в no-op.

#ifdef DEBUG
#define DEBUG_ONLY(x) x
#else
#define DEBUG_ONLY(x)
#endif
...
DEBUG_ONLY(red_led_toggle());


Ну да, синтаксис не очень… Но если мы компилируем без отладки — вызова red_led_toggle нет в принципе — и никаких проверок в этом месте тоже!

Для сравнения — в Java, даже в лучших реализациях отладочных библиотек (типа Log4j) проверка включения отладки делается в рантайме, и отладочный код всегда присутствует. Невозможно сделать его no-op на этапе компиляции (но там это обычно и не критично).
В свое время, посмотрев в какую сторону поехал C++, мы для больших систем перешли на Java. Хотя по #define, typedef и RAII скучали очень.

В итоге, C/C++ остался для программирования ближе к железу (контроллеры). Но там удается оставаться в рамках разумного подмножества языка.

Мы пришли к выводу, что рабочее время с большей пользой можно употребить на решение актуальных задач, а не на вспоминание местами странного синтаксиса шаблонов, угадывание причин ошибки из простыни вывода gcc на два экрана, или перекомпиляцию 90% кода проекта из-за незначительных изменений в одном файле.

Это не значит, что мы совсем не используем шаблоны по причине религиозного неприятия. Скорее, мы их используем в духе раннего Страуструпа для избавления от написания дублирующего кода (то есть генерации нескольких версий класса для разных входящих типов). А вот мета-программирование, шаблонная магия, бусты — извините, но нет.
С определенного момента, если программа на C++ делает нечто странное — для очистки совести приходится смотреть, что выдает gcc -S. В C и раннем C++ странное поведение программы гарантированно означало, что просто где-то упустили указатель. В целом, старые компиляторы более предсказуемы и более-менее просто переводят в машинные коды то, что написано разработчиком. Новые — с моей точки зрения, чрезмерно смелы в оптимизации. Я воспитан еще в той идеологии, что программисту виднее где оптимизировать, а где — нет. И да, я знаю что этот подход несовместим с шаблонами и автоматической кодогенерацией. Тем хуже для шаблонов.
Подбор человека в команду программистов сравним по сложности с подбором музыканта в оркестр (требуются и природные склонности, и усидчивость, и опыт). Вы удивитесь, узнав что делает дирижер (или ведущий музыкант группы, если оркестр большой) с кандидатом! Он просит его… сыграть! Да, блин! Оказывается, чтобы определить умеет ли человек играть на музыкальном инструменте (и на каком уровне он играет) — надо просить его делать именно это! И ни у одного вменяемого дирижера не хватит ума брать человека в оркестр на основании онлайн-теста типа: «Дека скрипки изготовлена из 'а' — бука, 'б' — сосны, 'в' — ясеня...». Потому что человек играть (!) у него будет на скрипке, а не дерево на зуб пробовать… И да — оценка качества игры субъективна. И да — отличный музыкант может не сыграться в оркестре. И да, если вы не можете, послушав, определить — стоит ли брать кандидата в оркестр (группу) — значит снимайте фрак, ломайте палочку и идите подметать улицу. Там тоже нужны профессионалы…

В целом, мое отношение к тестам простое: чудаки на букву «м» и приравненные к ним эффективные менеджеры хотят скрыть свою чудесатость (на ту же букву) и подстелить соломки под задницу. Для этого хорошо подходят формальные способы («не я дурака на работу принял, ТЕСТ нам его порекомендовал»). Поскольку онлайн-тестом проверить собственно способности к сложной интеллектуальной деятельности не получается — тесты проверяют вместо этого то, что можно легко проверить (вспоминаем Козьму Пруткова про мужика, который ищет потерянные ключи не там где потерял, а под фонарем — потому что там светлее!). Если вы уже относитесь к эффективным менеджерам, тесты — ваше всё! Если вам дело делать надо — посадите кандидата рядом, и пишите с ним код! И не вставайте, пока не поймете — хотите вы с ним этим же самым заниматься завтра, или нет? Хотите — значит берите. Не хотите — тогда посылайте. Для дела имеет значение только это.
Вообще, это напоминает мне задачу с двойным дном из физики с далеких школьных факультативов. Разбиралась с Ланкиной Маргаритой Павловной, низкий ей поклон и долгих лет жизни. «Надутый воздушный шарик сильно надавили пальцем. Где он лопнет?». Первый ответ интуитивный — где надавили. Потом вспоминаем про давление в среде газов (распределяется равномерно) — значит лопнет в произвольном месте (где тоньше или есть дефект). Потом еще раз вспоминаем что под пальцем оболочка проминается и растягивается локально (остальная часть шара в первом приближении так и остается сферической). Следовательно, в шаре без дефектов производства — наиболее вероятен прорыв где-то в зоне нажатия. Если взять школьный сборник задач (или ЕГЭ) — в нем ответ «под пальцем» будет, хи-хи, неправильным!
А это, в свою очередь, зависит от целей — для чего мы решаем эту задачу. Если это просто красивая оболочка для задачи двоичного кодирования и дерева выбора — тогда ответ очевиден. Если же вторая сторона хочет оценить способность кандидата решать задачи из жизни (а не из идеального мира) — уже не так очевиден. А главный недостаток задач заключается в том, что применяют на собеседовании их, как правило, бездумно и бессистемно. :-( В норме — задача должна быть поводом к разговору, и способом оценить умение кандидата думать и находить решения. В большинстве случаев, интервьюеры просто поставят плюсик если вы дадите ответ 10, или крестик — если любой другой…
Опять немного повредничаю на правах аналитика. :-) Крайне странно описывается процесс голосования. Если голосуют сразу все пираты (включая автора предложения), то разумно было бы написать «при равном числе голосов — предложение принимается». Ну потому что высказавший предложение пират голосует за него — это очевидно. «Решающий голос» обычно имеет место тогда, когда в группе имеется один не голосующий член (обычно — председатель), и если группа разделилась поровну — то выслушав аргументы каждой из сторон, ранее не голосовавший участник дает решающий голос. В общем, напиши мне такое клиент в пользовательской истории — я бы заставил привести уточняющие примеры, либо исправить формулировку, либо и то и другое сразу. :-) Также, непонятно насколько «кровожадны» пираты. Например, E понимает что в любом раскладе получит 1 монету. Если А предлагает ему 1 монету — он будет голосовать «за»? В принципе, он может проголосовать «против», пирата «А» скормят акулам, и ту же монетку ему предложит «Б». То есть ценность «остаться в живых» у пиратов прописана. А «оставить в живых» — нет! А это, на минуточку, может менять стратегии участников… Например, «A» в этой ситуации может предложить «E» две монетки, чтобы нарушить эквивалентность ситуаций у «E»…
Вот немного повредничаю со стороны не программиста, а аналитика. А никого не беспокоит факт, что из-за, э-э, конечной вместимости крысы — некоторые животные получат пониженную концентрацию действующего вещества по сравнению с оригинальной бутылкой? Если мне не изменяет память, то отравляющим веществам приписывается характеристика LD50 (измеряемая в (милли-, микро-) граммах на кг живого веса. И при этой концентрации вероятность гибели подопытного организма составляет 50%. Таким образом, исходное положение условия задачи «выпившая отравленное вино крыса умирает в течение часа» не эквивалентно положению «выпившая разбавленное отравленное вино крыса умирает в течение часа». Исходя из этого, я предложил бы использовать 20 крыс, причем вторую половину поить по принципу дополнения до единицы (одну напоили 100100111 — значит вторая получает вино из бутылок 011011000). Тогда в каждой паре через час должна подохнуть ровно одна крыса. Если в какой-то обе живы — значит концентрация сравнима (или даже ниже) LD50 и весь эксперимент ненадежен. Если обе подохли — вас подставили, и отравленных бутылок больше одной. :-) Или крыса подохла от случайного фактора. И дальше это плавно превращается в задачу о помехоустойчивом кодировании… В общем, если тестировать не математиков (у которых крысы, отрава и вино идеальные), а программистов — десять крыс маловато будет…
Глобально, это скорее хорошая новость. Потому что если продукт имеет низкое ROI, то он пытается решать малоценную проблему пользователей (они не готовы платить больше), привлекая редкие (и потому дорогие) ресурсы. Если указанное использование ресурсов прекратится, то последние освободятся и будут участвовать в решении более насущных проблем. Я думаю, что экономическая оправданность и легальность проекта/продукта автором статьи презюмируются. :-)

Information

Rating
1,608-th
Registered
Activity