All streams
Search
Write a publication
Pull to refresh
40
0
Павлов Николай Александрович @ZyXI

Инженер

Send message

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

Есть ещё возможный подход: написать генератор простых тестов вместо собственно простых тестов. Оправданность зависит от того, насколько сложно отлаживать «сложные» тесты и насколько сложно создать собственно генератор, а также для чего генератор будет полезен. При правильное реализации генератор должен генерировать достаточно хорошо читаемый и хорошо отлаживаемый boilerplate.


На C ещё был бы вариант с использованием ffi для написания тестов на языке более высокого уровня, но ни макросы ни шаблоны C++ так не протестируешь (хотя, может, clang предоставляет какое‐нибудь полезное API…).

То, что вы только что сказали — «вы не знаете, потому что это для вас не критично». То, что у вас недостаточно мотивации, чтобы узнать как автоматизировать настройку, не изменит того, что задача решаема, но вы не знаете как. Это никак не отдельный вариант.

Это относится к варианту «не знаете, как настроить». Нужные действия вполне можно автоматизировать, если вы действительно часто меняете аккаунты.

Вообще насколько я понимаю, в вашем случае приводить тип через str() не обязательно, поскольку ключи и так строки, но Python странный язык и допускает ключи других типов, числовые например. Поэтому я перестраховался, чтобы потом не ломать голову над странными ошибками. :)

Не вижу ничего странного, lua вот даже изменяемые таблицы в качестве ключей позволяет, тогда как Python пытается это предотвратить (не то чтобы я не смог запихнуть экземпляр изменяемого словаря в качестве ключа, если бы захотел: нужно только правильно определить хэш‐функцию (id пойдёт) и отношение равенства (словари равны, если это один и тот же словарь — так делает lua)). В C++ и rust вы тоже можете использовать практически любой тип в качестве типа ключа.

Мазохизм только если вы не знаете, как настроить окружение. Или не можете себе этого позволить (к примеру, потому что компьютер не ваш).

В POSIX shell без eval не очень много можно сделать. Я вот как‐то писал парсер --ключей: https://github.com/neovim/neovim/blob/c693afb8ac0aea94bc268f880511d7b7f3710d2c/scripts/pvscheck.sh#L76-L243 (вообще по‐хорошему надо бы в библиотеку оформить), там eval используется аж 19 раз (а вот echo — ровно один, и то в месте, где его вызов соответствует принципу «garbage in — garbage out»).

Видите там наверху цикл? Он категорически некорректен (не делает экранирование правильно), но там вполне можно было написать setcmd="set --" ; for arg ; do setcmd="$setcmd \"\$args$i\"", а потом использовать eval. Это если конечно данный set вообще нужен, может быть его лучше заменить на что‐то другое — я так далеко не копал.

Меня бы объяснение, что лямбда — это оказывается реализация паттерна стратегия только больше бы запутало. Лямбды — это ссылки на функции, функции определяются прямо на месте. Если вам всё ещё не понятно, представьте код лямбды, который вынесли в отдельный метод, а его вызов вставили в место вызова лямбды, но не в место её определения.


А зачем носятся с паттернами на хабре я не знаю, я как‐то прочитал их описание, понял, что мой мозг может сгенерировать что‐то подобное и без знания, что это какой‐то паттерн, и благополучно забыл бо́льшую часть прочитанного про паттерны. Учитывая, как «часто» мне потом приходилось слышать про паттерны от иностранных коллег во время собственно работы над OS проектами (Python сначала, C и lua сейчас), а также от русских коллег во время работы на собственно работе (LabVIEW, C и ассемблер), могу заключить, что паттерны мне нужны только на уровне «тот парень сказал „visitor“ и я понял, что он сказал». Конечно, ассемблер не особо располагает к применению шаблонов, но про остальные я такого не скажу.

Если можно получить bool унарным !, то не логичнее ли просто два раза инвертировать, если инвертировать не нужно, а bool нужен?

bool в C уже давно есть, и приведение к нему, явное или неявное, может выдать только true или false. Хотя вы всё ещё можете получить сложнодиагностируемую ошибку, если будете заполнять bool * какими‐нибудь двойками через memset() или любым другим из кучи обходных путей — минимально адресуемая единица памяти до сих пор байт и один bool (если он не битовое поле в структуре) будет занимать именно его.

Вообще‐то bool (как и true и false) по стандарту C99 именно макросы, раскрываемые как _Bool (1 и 0). Стандарт даже разрешает программе переопределять их (правда, сразу объявляет такую возможность устаревшей). Но #define bool int — это что‐то сомнительное, sizeof(_Bool) обычно единица, что означает, что вы не сможете с таким определением принимать bool * из библиотек на C99. А при другом соглашении о вызовах или порядке байт и просто bool передавать и принимать также не сможете.

Проблемы с условным переходом по идее должны решаться константой (C в комментарии). Изменение длительности цикла ожидания в данном случае только ограничивает максимальную скорость передачи.


Спасибо за беседу, интересно пообщаться с человеком с иным взглядом на проблему: я на работе (пока?) не пишу программы для микроконтроллеров с жёсткой экономией памяти (и отсутствием отдельного UART блока), но специфика работы (тестирование микросхем на радиационную стойкость) предполагает, что внутренняя частота во время испытаний уплывёт с немалой вероятностью, иногда даже намного сильнее, чем по datasheet’у. Поэтому автокорректировка на одной из сторон видится довольно полезной вещью — и если использовать строго стандартные скорости, то этой стороной будет менее ограниченный микроконтроллер. (Правда мы, если частота действительно уплывёт за границы, указанные в datasheet’е, можем написать, что схема не прошла испытания. Впрочем, часто внутреннего генератора нет или его не предполагается использовать.)

<C-r>=map(range(char2nr('a'), char2nr('z')) + range(char2nr('A'), char2nr('Z')), 'printf("case''%s'':return 1;break;", nr2char(v:val))')<CR>

Тактируемые циклы — это особенность программирования ПЛИС на LabVIEW, их тело запускается в начале такта и один запуск тела цикла гарантированно длится не более одного такта. На VHDL я не пишу, но знаю, что первая часть (запуск в начале такта) будет выглядеть примерно как process (clk) begin if rising_edge(clk) then {body} end if; end process;, а вторая часть вроде выглядит как TIMESPEC TS_CLK = PERIOD "clk" 100 MHz HIGH 50%; и идёт в отдельный файл (не VHDL), что в совокупности совершенно не выглядит как цикл (и потому я и написал уточнение).


По поводу калибровочных байт/длины нуля: я вижу их применимость в любом случае, когда осуществляется связь между двумя устройствами, частота приёма/передачи хотя бы одного из которых может изменяться во время работы. Вариант с длиной нуля специально придуман именно для случая, когда нельзя использовать калибровочные данные. У меня там действительно связь между ПЛИС и микроконтроллером, причём шлёт данные в основном второй, а подстраивается под него первая. В вашей ситуации приём с нулём будет выглядеть примерно так:


  1. Микроконтроллер определил, что ему нужно закалиброваться — например, по тому что он получает какой‐то бред. Или его только что включили, а создатель устройства решил не указывать конкретную частоту при создании прошивки. Важно, чтобы команды ему продолжали/начали слать, даже если другая сторона не понимает ответы микроконтроллера так же, как микроконтроллер не понимает команды.
  2. Он входит в калибровочный режим и начинает подсчитывать, насколько длинные нули ему приходят, запоминая только минимальное значение.
  3. Через некоторое время он выходит из калибровочного режима, вычисляет UART_DELAY на основе полученных данных (т.е. умножает длину нуля на коэффициент, предварительно проверив данные на адекватность) и далее использует его.

Требования к посылкам при этом минимальные: если во время «калибровочного режима» нужно, чтобы работал какой‐то ещё код, то «наличие последовательности 101 в посылке, в т.ч. подойдёт 10 и стоповый бит». Если не нужно, то ещё подойдёт посылка с единицей в наименее значимом бите: тогда можно не использовать прерывания. (Кстати, строка «стандарт» на момент написания комментария на всей странице встречается ровно два раза — все в том комментарии, на который я сейчас отвечаю.)


Конечно, «калибровочный режим» не нужен, если вы абсолютно уверены в том, что можете одной константой обеспечить приём и передачу на одной из стандартных скоростей в течении всего времени работы устройства и свободная память вам пригодится больше дополнительного запас прочности.


Про подстройку на основе длины нуля во время приёма: я подумал, как это будет выглядеть с программным UART и понял, что иметь такое в цикле задержки мне бы не понравилось. В целом примерно то же самое, что и с калибровочным режимом, только длина считается не во время калибровочного режима, а во время нормальной работы, а задержка вычисляется между приёмами.


PS: Чтобы исключить возможное недопонимание, привожу как я видел компенсацию дрейфа во время приёма по переходным состояниям: вместо nop’ов получение значения с порта, сравнение с сохранённым предыдущим, brne на команду вперёд и установка счётчика в UART_DELAY/2 − C (поправка на дополнительные затраты времени на установку счётчика).

А в Vim undo — это дерево. Иногда очень выручает, несмотря даже на то, что я не использую дополнения для визуализации undo состояний. Также позволяет иметь команды вида :earlier 10m (использовать состояние, которое было 10 минут назад по сравнению с текущим) — при использовании пользователем undo в этот промежуток времени без дерева (для корректной работы обыкновенного undo) и списка с временными метками, ссылающегося на узлы дерева (для корректной работы :earlier/:later) ничего не выйдет.

Понятно. Я эту часть видел, но от явления, названного «самосинхронизации», я ожидал либо чего‐то вроде изменения UART_DELAY (которая у вас сейчас константа) в зависимости от значения счётчика или же чего‐то вроде изменения счётчика задержки на половину от UART_DELAY — оба варианта когда значение на RX меняется во время задержки.

В вашем случае для получение длины нуля нужна будет любая последовательность «101» в середине или «10stop» в конце посылки, что тоже не является невероятным, но конкретно у меня FPGA и тактируемый цикл (в LabVIEW — цикл, гарантированно исполняющияся за один такт), поэтому вашей проблемы с началом посылки нет.


В любом случае, по переходным состояниям можно только компенсировать дрейф, но во время приёма и даже без потери посылки. По калибровочному байту можно получить значение скорости, имея смутное представление о том, каким оно должно быть, но требуется как‐то этот байт послать вместо чего‐то более полезного (я когда предлагал вариант с калибровочным байтом просто предполагал просто начинать с него каждую посылку). А по длине нуля можно калиброваться на произвольных посылках, опять же, имея только смутное представление о скорости — но что‐то получать в это время можно только если вы именно компенсируете так дрейф.

Information

Rating
Does not participate
Location
Москва, Москва и Московская обл., Россия
Date of birth
Registered
Activity