Приглашаем на летнюю конференцию KasperskyOS Night

Хотите подробнее узнать, как выглядит кибериммунная система KasperskyOS с точки зрения разработчика или специалиста по кибербезопасности и как писать под нее приложения? Тогда приглашаем вас 7 и 8 июля на онлайн-конференцию KasperskyOS Night 2021 Summer Edition по созданию кибериммунных IT-решений на KasperskyOS.

Цель KasperskyOS Night — дать участникам знания и инструменты для построения безопасных IT-систем будущего и рассказать про наш опыт разработки операционной системы и продуктов на ее основе.

Спикеры KasperskyOS Night — разработчики операционной системы «Лаборатории Касперского», которые поделятся подробностями о кибериммунном подходе к разработке, о возможностях портирования приложений и тестирования безопасности решений на KasperskyOS.

Вот подробная программа и тайминг мероприятия:

7 июля, среда

16:00

Открытие первого дня конференции

16:05

Вступительное слово

Евгений Касперский, генеральный директор «Лаборатории Касперского»

16:25

Всеобщая контейнеризация и доверие
Изоляцию в системах обеспечивают разными способами (виртуалки, докеры) и на разных уровнях, у всего есть плюсы и минусы по функциональности и производительности. Но как эти подходы выглядят с точки зрения кибербезопасности?

Сергей Рогачев, руководитель группы разработки ядра безопасной платформы

17:15

Assurance против Secure by design
Хочешь доверять — проверяй. Процесс проверки кода и доказательства его правильности и безопасности — мероприятие дорогое и не всегда физически возможное. Можно ли разумно «сэкономить», но не в ущерб безопасности?

Максим Донцов, руководитель группы аналитиков по информационной безопасности

17:45

Игры с POSIX
Опыт портирования симулятора Nintendo NES на KasperskyOS.

Артем Шишкин, старший разработчик группы разработки компонентов безопасной платформы

18:10

Как сделать продукт на KasperskyOS
Рассказ от первого лица: опыт разработки IoT-шлюза.

Михаил Демченко, разработчик группы разработки решений для интернета вещей

18:50

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

Вячеслав Борилин, менеджер по развитию технического сообщества

19:15

Вопросы-ответы

8 июля, четверг

16:00

Открытие второго дня конференции

16:05

Под капотом
Ошибки в софте для умных автомобилей — это очень нехорошо. Но как же иногда непросто найти их и исправить.

Василий Дыбала, ведущий разработчик группы разработки компонентов безопасной платформы

16:25

Безопасность и/или свобода — культура и процессы разработки
Разработчик средств IT-безопасности — фанатик устава в погонах или хипстер со смузи из Купертино? Поговорим о ценностях, процессах и ролях инженера, об удовлетворении от работы.

Максим Юдин, руководитель отдела разработки безопасной платформы

17:00

Персональные данные… приложений
Реализация персонального хранилища данных для приложения, защищенного от доступа со стороны других приложений. Как этого добиться с максимальной простотой и надежностью?

Максим Мымрин, ведущий разработчик группы разработки компонентов безопасной платформы

17:35

TLS как сервис KasperskyOS
TLS — суперважный для ИБ протокол, но очень часто трудности использования библиотек SSL/TLS- разработчиками, наоборот, порождают уязвимости в решениях. Обсудим, как можно сделать использование TLS проще и надежнее.

Дарья Зимарина, ведущий разработчик группы разработки компонентов безопасной платформы

18:15

Прокачиваем гипервизор, делимся результатами
Как мы для нужд IPC нашего гипервизора и работы «аватаров» начали расширять virtio vsock и контрибьютить патчи в сообщество ядра Linux.

Арсений Краснов, старший разработчик группы разработки безопасного гипервизора

18:45

Проектируем кибериммунность
Решаем задачки по кибериммунности, выигрываем и получаем призы!

Вячеслав Борилин, менеджер по развитию технического сообщества

19:10

Вопросы-ответы

Встречаемся онлайн с 16:00 до 20:00 7 и 8 июля, чтобы вместе изучать лучшие практики кибериммунных IT-решений на KasperskyOS! Приходите сами и зовите друзей!

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

Первая онлайн-конференция для разработчиков, посвященная операционной системе KasperskyOS, прошла в мае 2020 года. А записи с последней зимней версии KasperskyOS Night вы можете найти в нашем канале Youtube.

ссылка на оригинал статьи https://habr.com/ru/company/kaspersky/blog/565064/

Что оказалось самым важным в гарнитурах для колл-центров для самих операторов

Оказалось, качество звука — это не первое, на что операторы колл-центров обращают внимание. В смысле, что, конечно, оно отличается от устройства к устройству, но всё равно во многом зависит от линии, стабильности соединения и устройства абонента, а не от того, что на принимающей стороне. 

Мы начали тестировать разные гарнитуры, потому что постепенно расширяем линейку поставляемого оборудования для заказчиков. В общем, мы взяли много образцов у разных вендоров, отдали их нашим сотрудникам и сотрудницам поддержки первой линии «ой, у меня принтер не печатает» и попросили протестировать — чтобы затем понимать, что именно можно смело рекомендовать заказчикам. 

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

Решает эргономика. И операторы выбирают то, что не сплющивает голову к вечеру. 

Подавление внешних шумов

Из технических характеристик главным оказалось наличие шумодава в гарнитуре. Он там бывает двух типов:

  • Подавляющий звуки окружения оператора и оставляющий только его голос для абонента.

  • Подавляющий внешние шумы вокруг оператора для оператора, чтобы он мог лучше слушать клиента. 

То есть один для абонента, а другой для оператора.

Самая простая реализация шумодава для абонента такая: -10 децибел на всё из микрофона, и остаётся только голос оператора без дрели соседа, звуков разговоров других операторов и стука клавиш. Если не повезёт, голос тоже частично режется на некоторых звуках. 

Производители подходят к решению задачи по-разному. В нашем случае, например, на гарнитуре Plantronics EncorePro HW710 реализован вот такой микрофон:

В гарнитурах колл-центров шумодав обычно реализован как просто отсечка части спектра и узкая направленность. У HW710 (и вообще всей линейки начиная с неё) не слышно дыхания оператора, несмотря на то, что поп-фильтра на микрофоне, похоже, нет. Скорее всего, это из-за того, что он расположен на некотором удалении от потока выдыхаемого воздуха, но при этом довольно близко ко рту для достаточного звукового давления. Во всей 700-й серии везде телескопические штанги микрофона с точным его позиционированием. То есть 10 градусов влево-вправо — и оператора слышно хуже, чем при правильной позиции микрофона. Такая конструкция пока оптимальна, сделана по опыту использования микрофонов в КЦ военных, медицины и МЧС разных стран. 

Что же касается подавления шумов для оператора — тут есть совсем простые решения —от очень больших и плотных амбушюр (физически изолирующих уши) до старой доброй системы подачи противофазной звуковой волны. Это требует внешнего микрофона для записи звуков снаружи, но при этом не требует сложного аудиопроцессора, поэтому эти две системы бывают разделены. Ну и ещё от шумоподавления для оператора не требуется гасить вообще все внешние шумы — если рядом под окном лает собачка, где-то другой оператор говорит со своим абонентом и щёлкает мышка — абоненту не надо это слышать. А оператору достаточно уменьшить громкость этих звуков, чтобы повысить при этом разборчивость речи в наушниках. Пожалуй, именно поэтому эта часть системы крайне редко делается дорогой. Например, в Dect Savi 8220 шумодав на обратной волне есть, но он осознанно по опыту использования в разных КЦ сделан не полностью очищающим звуки снаружи, а очень сильно их приглушающим.

Ещё интересно, что у многих гарнитур в тестах были пульты на кабеле, на корпусе или на базовой станции (если это беспроводные гарнитуры) вот такого плана:

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

«Телефонное» или «трубочное» ухо

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

Вторая сторона держится на пластине над ухом как в «Звёздных войнах»: 
Вторая сторона держится на пластине над ухом как в «Звёздных войнах»: 

Это выглядит слегка странно, но если оператор часто общается с кем-то ещё в комнате рядом (с коллегами-операторами, например, либо с отправляемыми на выезды инженерами, как наши специалисты), то гарнитуру нужно постоянно снимать-надевать. Это оказывается очень неудобным после какого-то уровня эргономики гарнитур — но про это позже. И тогда операторы выбирают только один динамик. 

Ещё это важно в восточных контакт-центрах, поскольку такое крепление хорошо совместимо с паранджой. Но это не наш случай. 

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

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

Корпус гарнитуры

Есть колл-центры, где гарнитуры носят постоянно из-за потока звонков, а есть — где их снимают с одного уха или просто убирают между звонками. Во втором случае за день будет примерно 40-60 снятий и надеваний. Это значит, что если форма дужки или чего-то ещё хоть немного способна цеплять волосы, девушек-операторов с длинными волосами это будет невероятно бесить. 

Колл-центровские гарнитуры должны выдерживать жёсткую эксплуатацию, у них почти промышленное использование. Там, где у бытовой гарнитуры уже будет сломан провод, колл-центровская должна только начинать выглядеть слегка потрёпанной. Поэтому логичное решение заключается в увеличении корпуса для повышения надёжности. Но делать так нельзя — оператор сидит в гарнитуре весь день, и если она тяжелее буквально нескольких десятков граммов, то это становится очень заметно. Чем легче гарнитура, тем дольше её носят неподвижно, и тем меньше она в среднем изнашивается. В этом месте гарнитуры дорожают в сравнении с бытовыми: материалы заменяются на лёгкие прочные сплавы. Корпус уменьшается до воздушных дуг почти без потери прочности на разрушение и изгиб. 

Устройства Poly (бывшие Плантроникс) профессиональной серии нам очень понравились из-за того, что выглядят поначалу почти невесомыми (даже складные), но очень прочные. Это потому что вендор, вообще-то, сначала разрабатывал их для авиации, а потом при переносе в колл-центры оставил авиационные сплавы на местах соединений. Это делает корпус дороже, конечно, но зато должно очень сильно сказаться на сроке службы устройств. Тем не менее статистики у нас пока нет — если не считать того, что мы покрутили несколько гарнитур в стиральной машине в режиме отжима (зашитые в однослойную ткань) и посмотрели, что с ними стало. Методологии никакой, просто прикинули на пальцах.

Очень важен материал внешней обивки амбушюр для качества работы оператора. Если это нечто похожее на чёрную губку (выбор дешёвых устройств), то уши будут потеть, чесаться, неприятно уставать от прижатия к голове и покрываться чёрной крошкой со временем. Ещё операторы могут быть каждую смену разные на одном и том же месте, и встаёт вопрос гигиены. Пока лучше всего зарекомендовала себя кожа. Каких-то специальных универсальных анатомических форм нет, нужно подбирать гарнитуру под себя в идеале. 

Управление

Самая главная кнопка для наших тестеров — приём звонка. Вторая по важности — отключение микрофона. Они должны быть в логичном месте прямо под пальцами. 

Засада обычно в том, что если отключение микрофона прямо влияет на ту же систему, где находится кнопка и контроллер, и работает всегда, то вот приём звонка требует какой-никакой интеграции с устройством, куда гарнитура воткнута. И если стандартная телефония подцепляется более-менее интуитивно, то вот с приложениями вроде Телеграма или другими с возможностями звонков возникают проблемы. Вообще, переключить гарнитуру из одного режима в другой быстро — это отдельная фича, которая у разных производителей называется по-разному. Apple избаловала нас интуитивным переключением наушников между телефоном, планшетом, телевизором и ноутбуком, но вот в мире контакт-центров всё не так. Готовьтесь заходить на три клика в глубину приложения, чтобы просто позвонить по Zoom инженеру, используя ту же гарнитуру, по которой вы разговаривали с пользователем только что по внутриофисной телефонии. Особенно, если гарнитура беспроводная. 

На части гарнитур отключение микрофона тоже было программной функцией, и нажатие кнопки с пульта не отключало звук в Zoom. 

Беспроводная гарнитура

Беспроводные гарнитуры в среднем отличаются тремя важными вещами:

  • Качеством звука чуть хуже (из-за ещё одной цепочки передачи между базой и динамиками по DECT или Bluetooth). 

  • Зарядкой, за которой нужно следить. 

  • Большими танцами с бубном в плане настроек управления.

Но при этом они нравятся многим операторам — особенно в пандемию, когда колл-центры часто разъезжаются на удалёнку по домам. В такой гарнитуре можно спокойно ходить и делать свои домашние дела, что особенно важно, если есть паузы между звонками. Кнопка приёма звонка позволяет сначала принять звонок и начать разговаривать, а уже секунде на 20-й или 30-й садиться за компьютер. 

В тестах были Plantronics Savi W8220, они имеют заявленный ресурс зарядки 13 часов, и, похоже, это как раз та планка, которая нужна для восьмичасового рабочего дня — потому что через пару лет аккумулятор будет держать уже не так. Если на гарнитуре написано 9 часов — скорее всего, через год в обед или между звонками нужно будет ставить её на зарядку, что убивает часть преимуществ. Поэтому 12 и более. 

Эти W8220 позволяют отходить от рабочего места метров на 100 благодаря DECT-протоколу. Скорее всего, это было сделано для лучшей передачи звука. 

Беспроводные гарнитуры ещё хороши тем, что могут подключаться к разным устройствам. Вообще, обычно все гарнитуры поставляются с USB или одним из проприетарных коннекторов типа стандартного для Avaya. Есть переходники, но они стоят отдельных денег. 

Резюмируя

Операторы не слушают лютневую музыку XVIII века, а разговаривают с клиентами. Им важен шумодав, который скроет от них звуки бензопилы под окном, звуки лая соседской собаки из дома, большую часть хозяйственной деятельности домочадцев в комнатах рядом. Потом им важна разборчивость речи в наушнике: это в минимальной степени качество звука, во многом — качество отсечки внешних шумов вокруг оператора. Чем лучше слышно клиента и чем естественнее его голос (при включении шумодава часто искажается и несущая), тем меньше оператор устаёт. Конечно, это не исключает того, что абонент может вообще разговаривать с приборной панелью автомобиля, но в среднем так лучше. 

На втором месте эргономика и материалы — лёгкая, прочная и удобная гарнитура очень облегчает жизнь оператору и меньше амортизируется. Удобное управление помогает лучше работать. При этом — не деформируется со временем, то есть не сползает с головы. 

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

Ссылки:

Страница с тем, что мы проверили и рекомендуем.

— Подробное описание упомянутой проводной гарнитуры 710.

— Подробное описание упомянутой беспроводной гарнитуры 8220

— Моя почта для вопросов: DPesotskiy@croc.ru. Можно задавать вопросы про скидки, если что, такие устройства по стрит-прайсу почти никогда не продаются. 

ссылка на оригинал статьи https://habr.com/ru/company/croc/blog/565428/

Как Vim стал таким популярным

Вероятно, вы слышали о Vim — всемогущем текстовом редакторе, который используется повсюду и из которого люди время от времени не могут выбраться. Ваш знакомый DevOps-инженер рассказывает о том, насколько великолепен и быстр Vim, и вы, вероятно, подумали, что стоит его попробовать. Но как мы пришли ко всему этому? Каким образом одержимость Vim однажды охватила весь мир?

Если посмотреть на опрос StackOverflow 2019 года о самых популярных средах разработки, то Vim по-прежнему находится в нём на пятом месте — его используют примерно 25% веб-разработчиков. Пятое место — это довольно неплохо, учитывая, что первый релиз Vim произошёл почти 30 лет назад (да, мы не ошиблись — первая версия Vim появилась в 1991 году).

Чтобы лучше понять, как и почему Vim стал (и по-прежнему остаётся) столь популярным, нам нужно изучить, с чего всё начиналось. До vim существовал vi, до vi существовал ed. Что это за двух- и трёхбуквенные слова, и какая история за ними стоит? Давайте будем разбираться.

Перфокарты и строковые редакторы

Сегодня нам очевидны причины появления текстовых редакторов и полнофункциональных IDE
(Integrated Development Environment), но что было до них? Как вы, вероятно, знаете, на ранних этапах программирование заключалось в засовывании в компьютер листов картона с отверстиями (перфокарт). Очень важен был порядок ввода этих карт. Например, на фотографии ниже показаны 4,5 мегабайта данных, собранных в стопки из 62500 перфокарт. Представьте, что они упали и вам пришлось сортировать их заново.

С увеличением мощности и объёма накопителей компьютеров перфокарты становились всё более неэффективными, поэтому программирование эволюционировало. Со временем люди отказались от перфокарт, после чего настала эпоха «строковых редакторов». Отличным примером такого редактора является ed. Также стоит упомянуть предшественника ed под названием QED.

Работа со строковым редактором

Если у вас Linux или Mac OS, откройте терминал и введите ed. Откроется простой строковый редактор, который поприветствует вас полной тишиной. Если вас поразила эта находка (меня поразила), то можете немного поэкспериментировать с редактором. Чтобы вводить файл, наберите a и нажмите Enter, а затем пишите свой текст. Нажав ., а затем Enter, вы выйдете из режима добавления (вставки). Чтобы записать файл, можно ввести w myfile.txt и нажать Enter, а в конце ввести q и нажать Enter, чтобы выйти из редактора.

# ed a Hey there! I am using ed right now, how cool. OK, that's enough. . w myfile.txt 64 q


Как вы могли заметить, есть сходство между работой с ed и с современным vim. Если вы пользуетесь Vim, то довольно быстро освоите ed.

Отец Vim по имени Vi

После ed появился em«editor for mortals» («редактор для простых смертных»). Его функциональность схожа с ed, но он «менее загадочен» и создан для обычных людей. На основе кода em Билл Джой разработал ex, что расшифровывается как «extended ed» («расширенный ed»)! Он стал важным этапом, потому что наряду с предыдущими режимами у ex был режим visual («визуальный»), отображающий на экране весь файл. Вы можете спросить — а почему об этом не подумали раньше? В те времена визуальное отображение файлов на компьютерах было довольно сложным трюком, и многие считали его излишней тратой ресурсов. Однако в какой-то момент преимущества перевесили возражения, и отображение редактируемого файла на экране стало современным стандартом.

И так родился режим visual. Позже в операционных системах появился исполняемый файл vi, но мы по-прежнему можем пользоваться командами ex, вводя : в vi/vim. Редактор ex был выпущен в 1976 году, а исполняемый файл vi — в 1979 году. Сорок с лишним лет назад! Но как же появился vim?

Игра в имитацию

Пару лет спустя появилось множество клонов vi (мне больше всего нравится Elvis). Одним из них был «Vi Improved», созданный Брамом Моленаром — это имя вы видите, когда запускаете vim в терминале. Редактору vim удалось выделиться на фоне множества клонов vi. Брам взял клон vi под названием STEVIE (хорошее имя, кстати), и заметил, что в нём не хватает множества команд vi. Он добавил несколько новых функций и обеспечил его совместимость vi, после чего выпустил его под названием «Vi Imitation» (позже название сменилось на «Vi Improved»). Название vim появилось в версии 2.0, выпущенной в 1993 году, и сохраняется по сей день. Но как vim добился своей славы? На то время он имел множество великолепных возможностей и был совместим с vi. Функции и совместимость сделали его привлекательным для многих.

Полвека работы

Если посмотреть на важнейшие команды для работы с Vim: h, j, k, and l, то все они уходят корнями в эпоху vi. В то время на клавиатуре Билла Джоя не было курсорных клавиш. Кроме того, клавиша ESC находилась на месте современной TAB. Посмотрите, как это выглядело тогда:

Команды замены текста, например, :%s/text_to_replace/text_to_replace_it_with/, тоже пришли из той эпохи. Необходимость ввода : для выполнения команды — это ответ на полную тишину ed, которая встречала пользователей, впервые вошедших в редактор.

Этим я хочу показать, что vim стал результатом более чем полувекового накопления хороших идей, а также серьёзных усилий по сохранению обратной совместимости. Да, полезные функции, возможно, сделали Vim знаменитым. Но актуальность ему обеспечивала совместимость практически с любым редактором. Сегодня в SSH можно запустить Vim, или, по крайней мере, Vi. Это одна из самых важных причин изучать его сегодня.

Если вы не взаимодействуете со множеством серверов и не работаете на них с файлами, то смысл повсеместного наличия Vim вас не коснётся. Однако он всё равно может оказаться для вас ценным.

Заключение

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

Кто знает, возможно режим Vim есть в вашей IDE или VSCode. Я не хочу сказать, что Vim должен заменить всё, чем вы сейчас пользуетесь, но его изучение и настройка улучшат ваши навыки. С другой стороны, самое главное — не редактор, а то, что вы с его помощью делаете!

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


На правах рекламы

Аренда облачного сервера на Linux или Windows — выбирайте одну из предустановленных ОС, либо устанавливайте из своего образа.

Подписывайтесь на наш чат в Telegram.

ссылка на оригинал статьи https://habr.com/ru/company/vdsina/blog/565320/

Металингвистический совратитель Си. Опус III: Садистская машина

>> Осторожно, модерн! — Начальник

Предисловие

В предыдущем опусе сквозь человеческие и зверинные кости явилось устройство для обобщённой макрорекурсии в стиле передачи продолжений. Беда в том, что эта концептуально изящная конструкция не приспособлена под реальность; изуродование препроцессора повлекло за собой изуродование мысли инженера: теперь творцу приходится выражаться в так называемом "перевёрнутом" стиле потока исполнения.

Сегодня речь пойдёт о метаязыке Metalang99 — это надстройка над CPS-двигателем макрорекурсии с привычным потоком исполнения. Его суть — мимикрировать под метаязык стандартного препроцессора Си, но в то же время разрешать обобщённую рекурсию — то, чего так не хватало в написании хоть сколько-то нетривиальных мета-алгоритмов.

Опусография

Содержание

  • Садизм I: Переосмысление двигателя рекурсии
  • Садизм II: Прозрение
  • Садизм III: Синтаксис и садистская машина
  • Садизм IV: Реализация
  • Садизм V: Примеры работы

Садизм I: Переосмысление двигателя рекурсии

CPS двигатель рекурсии, представленный в предыдущем опусе, избыточен: мы переопределяли одни и те же макросы по многу раз, что вылилось в чрезмерную сложность реализации и разбухание исходного кода Metalang99. Новая реализация элегантна; она лишена недостатков предыдушей, и, более того, сделана переиспользуемой: вместо того, чтобы увеличивать максимальную глубину рекурсии посредством добавления новых макросов, мы можем одним ML99_PRIV_REC_EXPAND(...) увеличить её на 1024 шага! Об этом — в настоящей секции.

Начнём сначала. Единственный способ заполучить механизм для рекурсии фиксированной глубины в препроцессоре C/C++ (а этот подвид — самый обобщённый из всех возможных видов рекурсий, поддающихся выражению на языке макросов) — это выразить данный механизм на CPS, подвиде потока исполнения программы, в котором продолжения материализуется в совершенно первоклассную лингвистическую абстракцию — функцию, и передаются друг посредством друга, чтобы продолжить исполнение программы на данном месте (считать — перепрыгнуть в следующую отметку исполнения). Так как мы работаем с макросистемой C/C++, то аналог функций — макрос, этой абстракцией мы и будем пользоваться. Мы будем передавать идентификатор первого рекурсивного макроса в следующий рекурсивный макрос, и тот, в свою очередь, по мере завершения своего исполнения будет передавать поток исполнения в первый рекурсивный макрос, так называемое продолжение. Нам также понадобиться терминальное продолжение — ML99_PRIV_REC_STOP — оно будет являться продолжением, поставляющимся в самый-самый первый рекурсивный макрос, ведь логично, что никуда, кроме как закончить исполнение программы на данном месте, нам перепрыгивать не нужно. Жилка двигателя рекурсии — это цепочка из макросов-раскрывателей следующего вида:

#define ML99_PRIV_REC_0(choice, ...)    ML99_PRIV_REC_NEXT(1, choice)(__VA_ARGS__) #define ML99_PRIV_REC_1(choice, ...)    ML99_PRIV_REC_NEXT(2, choice)(__VA_ARGS__) #define ML99_PRIV_REC_2(choice, ...)    ML99_PRIV_REC_NEXT(3, choice)(__VA_ARGS__) #define ML99_PRIV_REC_3(choice, ...)    ML99_PRIV_REC_NEXT(4, choice)(__VA_ARGS__) #define ML99_PRIV_REC_4(choice, ...)    ML99_PRIV_REC_NEXT(5, choice)(__VA_ARGS__) #define ML99_PRIV_REC_5(choice, ...)    ML99_PRIV_REC_NEXT(6, choice)(__VA_ARGS__) #define ML99_PRIV_REC_6(choice, ...)    ML99_PRIV_REC_NEXT(7, choice)(__VA_ARGS__) #define ML99_PRIV_REC_7(choice, ...)    ML99_PRIV_REC_NEXT(8, choice)(__VA_ARGS__) #define ML99_PRIV_REC_8(choice, ...)    ML99_PRIV_REC_NEXT(9, choice)(__VA_ARGS__) #define ML99_PRIV_REC_9(choice, ...)    ML99_PRIV_REC_NEXT(10, choice)(__VA_ARGS__) ...

И так до 1023:

#define ML99_PRIV_REC_1023              ML99_PRIV_REC_DEFER(ML99_PRIV_REC_0_HOOK)()  #define ML99_PRIV_REC_0_HOOK() ML99_PRIV_REC_0

И тут происходит следующее: мы откладываем раскрытие ML99_PRIV_REC_0 на ровно одно препроцессорное раскрытие (ведь иначе ML99_PRIV_REC_0 автоматически заблокируется препроцессором), и в конце всей этой цепочки, если только она не прервалась где-то раньше вследствие завершения исполнения метапрограммы, в конце этой всей цепочки мы получаем что-то вроде ML99_PRIV_REC_0(...), где ... — оставшаяся часть метапрограммы — то бишь мы получаем ровно то, что специфицировали в начале, но уже с редуцированной частью входа:

#define ML99_PRIV_REC_UNROLL(...) ML99_PRIV_REC_UNROLL_AUX(__VA_ARGS__)  #define ML99_PRIV_REC_UNROLL_AUX(choice, ...) \     /* Approximately 1024 * 16 reduction steps. */ \     ML99_PRIV_REC_EXPAND( \     ML99_PRIV_REC_EXPAND( \     ML99_PRIV_REC_EXPAND( \     ML99_PRIV_REC_EXPAND( \     ML99_PRIV_REC_EXPAND( \     ML99_PRIV_REC_EXPAND( \     ML99_PRIV_REC_EXPAND( \     ML99_PRIV_REC_EXPAND( \     ML99_PRIV_REC_EXPAND( \     ML99_PRIV_REC_EXPAND( \     ML99_PRIV_REC_EXPAND( \     ML99_PRIV_REC_EXPAND( \     ML99_PRIV_REC_EXPAND( \     ML99_PRIV_REC_EXPAND( \     ML99_PRIV_REC_EXPAND( \     ML99_PRIV_REC_EXPAND( \         ML99_PRIV_REC_NEXT(0, choice)(__VA_ARGS__) \     ))))))))))))))))

Я говорю о ML99_PRIV_REC_NEXT(0, choice)(__VA_ARGS__). Тут мы можем наблюдать цепочку ML99_PRIV_REC_EXPAND, как я пояснил ранее, и она превосходно сочетается с тем ML99_PRIV_REC_DEFER, который мы указали в самом начале: ML99_PRIV_REC_DEFER откладывает раскрытие, чтобы вся предыдущая цепочка макросов-раскрывателей не заблокировалась препроцессором, а ML99_PRIV_REC_EXPAND находится ровно в том месте, где такая блокировка невозможна по определению: ML99_PRIV_REC_UNROLL_AUX не является вызванным данной цепочкой раскрывателей, а наоборот — он вызывает цепочку раскрывателей. Мы изворачиваем цепочку кишками наружу и издеваемся над ней столько, сколько нам захочется — а именно 16 раз по 1024 раскрытия (2^14).

Последнее, что заслуживает внимания в самом двигателе — так это макрос ML99_PRIV_REC_NEXT:

#define ML99_PRIV_REC_NEXT(next_lvl, choice)   ML99_PRIV_REC_NEXT_##choice(next_lvl) #define ML99_PRIV_REC_NEXT_0continue(next_lvl) ML99_PRIV_REC_##next_lvl #define ML99_PRIV_REC_NEXT_0stop(_next_lvl)    ML99_PRIV_REC_HALT  #define ML99_PRIV_REC_HALT(...) __VA_ARGS__

Каждый макрос-раскрыватель принимает формальный параметр choice, который или 0continue, или 0stop (управляющие рекурсией макросы для использования в пользовательском коде):

#define ML99_PRIV_REC_CONTINUE(k)      0continue, ML99_PRIV_REC_DEFER(k##_HOOK)() #define ML99_PRIV_REC_STOP(_k_cx, ...) 0stop, __VA_ARGS__ #define ML99_PRIV_REC_STOP_HOOK()      ML99_PRIV_REC_STOP

Этот choice сопоставляется с образом в привычной манере: если он 0continue, то переходим к следующему в цепочке макросу-раскрывателю, а если 0stop, значит было вызвано терминальное продолжение ML99_PRIV_REC_STOP —тогда просто раскрываемся в то, что осталось (ML99_PRIV_REC_HALT). Заметьте, что все неиспользуемые ML99_PRIV_REC_EXPAND не влияют на результат, ведь мы всё равно всё раскрыли — они скорее выступают за ID-функции.

Дабы не быть голословным, вот пример использования:

 #define F(acc, i)          ML99_PRIV_IF(ML99_PRIV_NAT_EQ(i, 10), F_DONE, F_PROGRESS)(acc, i) #define F_DONE(acc, _i)    ML99_PRIV_REC_CONTINUE(ML99_PRIV_REC_STOP)(~, acc) #define F_PROGRESS(acc, i) ML99_PRIV_REC_CONTINUE(F)(acc##X, ML99_PRIV_INC(i)) #define F_HOOK()           F  #define XXXXXXXXXX 678  ML99_ASSERT_UNEVAL(ML99_PRIV_REC_UNROLL(F(, 0)) == 678);

Данный код конкатенирует X ровно 10 раз, и в конечном итоге получается 678. Макросы ML99_PRIV_NAT_EQ и ML99_PRIV_INC обладают говорящими за себя именами: первый сравнивает два натуральных числа, второй инкрементирует.

Садизм II: Прозрение

Писать в стиле передачи продолжений — козлёночком стать. Нам нужно компилировать прямой поток исполнения в перевёрнутый. Как это сделать, если макрос не позволяют расширяться в другие макросы? Тут нам помогут три знаменитые проекции Футамуры, тесно связанные с частичными вычислениями; а именно, мы воспользуемся первой проекцией:

Specializing an interpreter for given source code, yielding an executable.

В данном определении три существенных понятия: interpreter, source code и executable:

  • Interpreter — это нечто, что интерпретирует прямой поток исполнения. Более того, интерпретатор должен быть написан на CPS (об этом в следующих пунктах).
  • Source code — это последовательность термов в прямом потоке исполнения.
  • Executable — это то, что мы получим, если специализируем interpreter на source code. Поэтому интерпретатор должен быть написан на CPS.

Итак, наша цель — написать интерпретатор на стиле передачи продолжений (CPS), интерпретирующий метапрограммы, которым естественнен прямой поток исполнения, метапрограммы, входящие в наш будущий функциональный язык для метапрограммирования. Только так мы можем обхитрить препроцессор и скомпилировать то, что по логике нельзя. В следующей секции данный метаязык мы определяем формально: и его синтаксис, и его семантику.

Садизм III: Синтаксис и садистская машина

Синтаксис языка должен отражать то, что мы хотим в итоге получить. Мы хотим обрабатывать вызовы, возможно рекурсивные. Абстрактная машина предписывает правила, по которым синтаксические элементы редуцируются в конечный результат. Не вижу смысла заново расписывать тут всё то, что предписано в спецификации Metalang99. Прочитали — перейдём к реализации.

Садизм IV: Реализация

Теперь самая мякотка! Глянем на исходный код CPS-style интерпретатора:

[eval/eval.h]

Показать малыша

 #include <metalang99/priv/util.h>  #include <metalang99/eval/acc.h> #include <metalang99/eval/rec.h> #include <metalang99/eval/syntax_checker.h> #include <metalang99/eval/term.h>  #define ML99_PRIV_EVAL(...) \     ML99_PRIV_REC_UNROLL(ML99_PRIV_EVAL_MATCH( \         ML99_PRIV_REC_STOP, \         (~), \         0fspace, \         ML99_PRIV_EVAL_ACC, \         __VA_ARGS__, \         (0end, ~), \         ~))  // Recursion hooks { #define ML99_PRIV_EVAL_MATCH_HOOK()         ML99_PRIV_EVAL_MATCH #define ML99_PRIV_EVAL_0v_K_HOOK()          ML99_PRIV_EVAL_0v_K #define ML99_PRIV_EVAL_0args_K_HOOK()       ML99_PRIV_EVAL_0args_K #define ML99_PRIV_EVAL_0op_K_HOOK()         ML99_PRIV_EVAL_0op_K #define ML99_PRIV_EVAL_0callUneval_K_HOOK() ML99_PRIV_EVAL_0callUneval_K // }  #define ML99_PRIV_EVAL_MATCH(k, k_cx, folder, acc, head, ...)      ML99_PRIV_CHECK_TERM(head, ML99_PRIV_TERM_MATCH) \     (head, ML99_PRIV_EVAL_)(k, k_cx, folder, acc, (__VA_ARGS__), ML99_PRIV_EVAL_TERM_DATA head)  // Reduction rules { #define ML99_PRIV_EVAL_0v          ML99_PRIV_REC_CONTINUE(ML99_PRIV_EVAL_0v_K) #define ML99_PRIV_EVAL_0args       ML99_PRIV_REC_CONTINUE(ML99_PRIV_EVAL_0args_K) #define ML99_PRIV_EVAL_0op         ML99_PRIV_REC_CONTINUE(ML99_PRIV_EVAL_0op_K) #define ML99_PRIV_EVAL_0callUneval ML99_PRIV_REC_CONTINUE(ML99_PRIV_EVAL_0callUneval_K)  #define ML99_PRIV_EVAL_0fatal(...) ML99_PRIV_EVAL_0fatal_AUX(__VA_ARGS__) #define ML99_PRIV_EVAL_0fatal_AUX(_k, _k_cx, _folder, _acc, _tail, f, message) \     ML99_PRIV_REC_CONTINUE(ML99_PRIV_REC_STOP)((~), !"Metalang99 error" (f): message)  #define ML99_PRIV_EVAL_0abort(_k, k_cx, folder, acc, _tail, ...) \     ML99_PRIV_REC_CONTINUE(ML99_PRIV_EVAL_MATCH) \     (ML99_PRIV_REC_STOP, (~), 0fspace, ML99_PRIV_EVAL_ACC, __VA_ARGS__, (0end, ~), ~)  #define ML99_PRIV_EVAL_0end(k, k_cx, _folder, acc, _tail, _) \     ML99_PRIV_REC_CONTINUE(k) \     (ML99_PRIV_EXPAND k_cx, ML99_PRIV_EVAL_ACC_UNWRAP acc) // } (Reduction rules)  // Continuations { #define ML99_PRIV_EVAL_0v_K(k, k_cx, folder, acc, tail, ...) \     ML99_PRIV_MACHINE_REDUCE( \         k, \         k_cx, \         folder, \         ML99_PRIV_EVAL_##folder(acc, __VA_ARGS__), \         ML99_PRIV_EXPAND tail)  #define ML99_PRIV_EVAL_0args_K(k, k_cx, folder, acc, tail, op, ...) \     ML99_PRIV_MACHINE_REDUCE( \         ML99_PRIV_EVAL_0callUneval_K, \         (k, k_cx, folder, acc, tail, op), \         0fcomma, \         ML99_PRIV_EVAL_ACC_COMMA_SEP, \         __VA_ARGS__, \         (0end, ~), \         ~)  #define ML99_PRIV_EVAL_0op_K(k, k_cx, folder, acc, tail, op, ...) \     ML99_PRIV_MACHINE_REDUCE( \         ML99_PRIV_EVAL_0callUneval_K, \         (k, k_cx, folder, acc, tail), \         0fcomma, \         ML99_PRIV_EVAL_ACC_COMMA_SEP, \         op, \         __VA_ARGS__, \         (0end, ~), \         ~)  #define ML99_PRIV_EVAL_0callUneval_K(k, k_cx, folder, acc, tail, evaluated_op, ...) \     /* If the metafunction `evaluated_op` expands to many terms, we first evaluate these terms and \      * accumulate them, otherwise, we just paste the single term with the rest of the tail. This \      * optimisation results in a huge performance improvement. */ \     ML99_PRIV_IF( \         ML99_PRIV_CONTAINS_COMMA(evaluated_op##_IMPL(__VA_ARGS__)), \         ML99_PRIV_EVAL_0callUneval_K_REGULAR, \         ML99_PRIV_EVAL_0callUneval_K_OPTIMIZED) \     (k, k_cx, folder, acc, tail, evaluated_op##_IMPL(__VA_ARGS__))  #define ML99_PRIV_EVAL_0callUneval_K_OPTIMIZED(k, k_cx, folder, acc, tail, body) \     ML99_PRIV_MACHINE_REDUCE(k, k_cx, folder, acc, body, ML99_PRIV_EXPAND tail)  #define ML99_PRIV_EVAL_0callUneval_K_REGULAR(k, k_cx, folder, acc, tail, ...) \     ML99_PRIV_MACHINE_REDUCE( \         ML99_PRIV_EVAL_0v_K, \         (k, k_cx, folder, acc, tail), \         0fspace, \         ML99_PRIV_EVAL_ACC, \         __VA_ARGS__, \         (0end, ~), \         ~)  #define ML99_PRIV_MACHINE_REDUCE(...) ML99_PRIV_EVAL_MATCH(__VA_ARGS__) // } (Continuations)

Он почти 1-в-1 отображает редукционную семантику, описанную в спецификации, за исключением лишь одной оптимизации. Как вы можете наблюдать, мы параметризируем внутренние функции интерпретатора (ВФИ) по k, k_cx, folder, acc, tail:

  • k — следующее продолжение.
  • k_cx — среда захвата следующего продолжения.
  • folder — макрос для левой свёртки редуцированного терма вместе с остальными редуцированными термами.
  • acc — аккумулятор, остальные редуцированные термы.
  • tail — оставшиеся термы для редукции.

Элегантная концепция — folder. Она позволяет абстрагироваться от comma-separated и whitespace-separated термов: если мы хотим первое, мы указываем 0fspace, если второе — 0fcomma (они впоследствии склеиваются с ML99_PRIV_EVAL и получаются обычные макросы):

#define ML99_PRIV_EVAL_ACC           (, ) #define ML99_PRIV_EVAL_ACC_COMMA_SEP ()  #define ML99_PRIV_EVAL_0fspace(acc, ...) (ML99_PRIV_EXPAND acc __VA_ARGS__) #define ML99_PRIV_EVAL_0fcomma(acc, ...) (ML99_PRIV_EXPAND acc, __VA_ARGS__)  #define ML99_PRIV_EVAL_ACC_UNWRAP(_emptiness, ...) __VA_ARGS__

Жила интерпретатора — опять же, сопоставление с образом. На вход подаётся терм, который сопоставляется с образом на несколько ВФИ, обрабатывающих соответствующий тип терма: ML99_PRIV_EVAL_0fatal, ML99_PRIV_EVAL_0abort, ML99_PRIV_EVAL_0v, ML99_PRIV_EVAL_0args, ML99_PRIV_EVAL_0op, ML99_PRIV_EVAL_0callUneval и ML99_PRIV_EVAL_0end. Последний — это "искусственый" терм, добавляющийся в последовательность настоящих пользовательских термов, чтобы обнаружить конец данной последовательности. Как правило, каждая такая ВФИ раскрывается в ML99_PRIV_MACHINE_REDUCE:

#define ML99_PRIV_MACHINE_REDUCE(...) ML99_PRIV_EVAL_MATCH(__VA_ARGS__)

Т.е. выполняет один шаг редукции. Заметьте, что если бы мы просто так вызвали ML99_PRIV_MACHINE_REDUCE, то ML99_PRIV_EVAL_MATCH бы просто заблокировался как рекурсивный вызов. Вместо этого, мы материализуем каждую ВФИ в продолжение! Таким образом, ВФИ формируют правильную рекурсию и каждая ВФИ сопоставляется ровно с одним шагом редукции. Пример с v(...) (нормальной формы терма):

#define ML99_PRIV_EVAL_0v_K_HOOK()          ML99_PRIV_EVAL_0v_K  ...  #define ML99_PRIV_EVAL_0v          ML99_PRIV_REC_CONTINUE(ML99_PRIV_EVAL_0v_K)  ...  #define ML99_PRIV_EVAL_0v_K(k, k_cx, folder, acc, tail, ...) \     ML99_PRIV_MACHINE_REDUCE( \         k, \         k_cx, \         folder, \         ML99_PRIV_EVAL_##folder(acc, __VA_ARGS__), \         ML99_PRIV_EXPAND tail)

Сама функция сопоставления ML99_PRIV_EVAL_MATCH вызывает ML99_PRIV_EVAL_0v, которая раскрывается в собственное продолжение, которое вызывается и редуцирует терм, и так по циклу. Интерпретатор на CPS — как и было сказано ранее.

Стоит отметить, что ML99_PRIV_EVAL_MATCH каждый раз совершают false negative проверку на синтаксическое соответствие:

#define ML99_PRIV_EVAL_MATCH(k, k_cx, folder, acc, head, ...) \     ML99_PRIV_CHECK_TERM(head, ML99_PRIV_TERM_MATCH) \     (head, ML99_PRIV_EVAL_)(k, k_cx, folder, acc, (__VA_ARGS__), ML99_PRIV_EVAL_TERM_DATA head)

Так как термы раскрываются в нечто (kind, ...):

#define ML99_call(op, ...) (ML99_PRIV_IF(ML99_PRIV_IS_UNTUPLE(op), 0args, 0op), op, __VA_ARGS__) #define ML99_callUneval(ident, ...) (0callUneval, ident, __VA_ARGS__) #define v(...) (0v, __VA_ARGS__) #define ML99_fatal(f, ...) (0fatal, f, #__VA_ARGS__) #define ML99_abort(...) (0abort, __VA_ARGS__)

То этому ML99_PRIV_CHECK_TERM остаётся лишь проверить данную форму:

#define ML99_PRIV_CHECK_TERM(term, default) \     ML99_PRIV_IF(ML99_PRIV_IS_UNTUPLE(term), ML99_PRIV_SYNTAX_CHECKER_EMIT_ERROR, default) #define ML99_PRIV_SYNTAX_CHECKER_EMIT_ERROR(term, ...) \     ML99_PRIV_SYNTAX_ERROR(term) \     /* Consume arguments passed to ML99_PRIV_TERM_MATCH, see eval.h. */      ML99_PRIV_EMPTY  #define ML99_PRIV_SYNTAX_ERROR(invalid_term) \     ML99_PRIV_REC_CONTINUE(ML99_PRIV_REC_STOP)((~), !"Metalang99 syntax error": {invalid_term})

Это не предотвращает все синтаксические ошибки, но предотвращает многие:

// !"Metalang99 syntax error": {123} ML99_EVAL(123)

Или более продвинутый пример с Datatype99:

// !"Metalang99 error" (ML99_assertIsTuple): "Bar(int) must be (x1, ..., xN)" datatype(A, (Foo, int), Bar(int));

Более того, интерпретатор может прервать исполнение программы по инициативе пользователя, посредством ML99_fatal или ML99_abort:

(Пример с ML99_abort)

#define F_IMPL(x, y, z) v(x + y + z)  // abc ML99_EVAL(ML99_call(F, v(1, 2), ML99_abort(v(abc))))

(Пример с ML99_fatal)

// !"Metalang99 error" (F): "the description of your error" ML99_EVAL(ML99_fatal(F, the description of your error))

(Пример с ML99_fatal, списки)

// !"Metalang99 error" (ML99_listHead): "expected a non-empty list" ML99_EVAL(ML99_listHead(ML99_nil()))

Более подробно — в главе Testing, debugging, and error reporting пользовательского туториала по Metalang99.

Отлично. Все основные концепции про интерпретатор мы разобрали. Осталось теперь посмотреть на нашего злого пупса в деле.

Садизм V: Примеры работы

Обход двоичного дерева во время компиляции

[examples/binary_tree.c]

#include <metalang99.h>  #define Leaf(x)              ML99_choice(v(Leaf), x) #define Node(lhs, data, rhs) ML99_choice(v(Node), lhs, data, rhs)  #define SUM(tree)                     ML99_match(tree, v(SUM_)) #define SUM_Leaf_IMPL(x)              v(x) #define SUM_Node_IMPL(lhs, data, rhs) ML99_add3(SUM(v(lhs)), v(data), SUM(v(rhs)))  /*  *         4  *        / \  *       /   \  *      /     \  *     2       6  *    / \     / \  *   1   3   5   7  */ #define TREE Node(Node(Leaf(v(1)), v(2), Leaf(v(3))), v(4), Node(Leaf(v(5)), v(6), Leaf(v(7))))  ML99_ASSERT_EQ(SUM(TREE), v(28));  int main(void) {}

Функция Аккермана

[examples/ackermann.c]

#include <metalang99.h>  #define ack(m, n) ML99_natMatchWithArgs(m, v(ack_), n)  #define ack_Z_IMPL(n)      ML99_inc(v(n)) #define ack_S_IMPL(m, n)   ML99_natMatchWithArgs(v(n), v(ack_S_), v(m)) #define ack_S_Z_IMPL(m)    ack(v(m), v(1)) #define ack_S_S_IMPL(n, m) ack(v(m), ack(ML99_inc(v(m)), v(n)))  ML99_ASSERT_EQ(ack(v(0), v(0)), v(1)); ML99_ASSERT_EQ(ack(v(0), v(1)), v(2)); ML99_ASSERT_EQ(ack(v(0), v(2)), v(3));  ML99_ASSERT_EQ(ack(v(1), v(0)), v(2)); ML99_ASSERT_EQ(ack(v(1), v(1)), v(3)); ML99_ASSERT_EQ(ack(v(1), v(2)), v(4));  ML99_ASSERT_EQ(ack(v(2), v(0)), v(3)); ML99_ASSERT_EQ(ack(v(2), v(1)), v(5)); ML99_ASSERT_EQ(ack(v(2), v(2)), v(7));  int main(void) {}

Генерация Duff’s device

(Duff’s device — классический трюк слияния switch-statement с циклами для автоматической развёртки циклов.)

Исходный код примера >>.

Нетипизированное лямбда-исчисление

Исходный код примера >>.

Заключение

Наше путешествие длиною в три опуса подошло к концу. Начинали мы с тривиальных препроцессорных идиом, закончили CPS-style интерпретатором. Данная технология оказалась довольно работоспособной: она дала возможность появиться таким абстракциям, как Datatype99 и Interface99 — алгебраические типы данных и удобная генерация интерфейсов для голого C99. (Рекомендую посмотреть README.md, в том числе и FAQ всех трёх проектов — там отвечены и такие вопросы, как "Зачем использовать C99?", "Почему не сторонние кодогенераторы", и т.д.).

ссылка на оригинал статьи https://habr.com/ru/post/523688/

Хранилище внешних обработок 1С на python

Хотелось бы поделиться своим опытом создания простенького хранилища внешних обработок 1С. Если Вы программист 1С и ваши коллеги постоянно затирают вам код внешних обработок то это статья для вас!

Перейдем сразу к описанию:

Алгоритм работы очень простой

Захват обработки

  1. Заходим в каталог рабочей базы где хранятся внешние обработки

  2. Кликаем правой кнопкой на файл с расширением .epf (внешняя обработка)

  3. В появившемся меню жмакаем «Захватить»

  4. Файл помечается «только на чтение»

  5. Файл копируется к вам в указанную папку см. «UsersConfig.json»

  6. Данные файла сохраняются в «ЗахваченныеФайлы.json»

  7. Путь к сохраненному файлу копируется в буфер, т. ч. можно сразу открывать конфигуратор и работать.

Примечание: захватить обработку можно без копирования файла!

Поместить обработку

  1. Поместить обработку можно как из каталога рабочей базы, так и из того, что вы указали в «UsersConfig.json»

  2. Кликаем правой кнопкой на файл с расширением .epf (внешняя обработка)

  3. В появившемся меню жмакаем «Поместить»

  4. Свойство «только на чтение» снимается

  5. Файл копируется в каталог рабочей базы

  6. Данные о файле удаляются из «ЗахваченныеФайлы.json»

Примечание: поместить обработку можно без копирования файла!

Что надо сделать что бы это заработало?

  1. Установить Python всем заинтересованным пользователям

  2. Python должен быть одной версии и установлен по одинаковому пути у всех пользователей, например «C:\Program Files\Python39»

  3. Создать проект (python) хранилища на локальной машине. Рекомендую использовать PyCharm.

  4. Исходники проекта можно скачать тут.

  5. Не забываем накатить requirements.txt -> pip install requirements.txt (в терминале PyCharm) !!!

  6. Копируем файлы проекта в общую папку, куда все (ваши коллеги) имеют доступ, копируем вместе с venv (виртуальное окружение python)

  7. Добавим в контекстное меню для файлов с расширением .epf две кнопки захватить/поместить. Необходимо воспользоваться файлом Добавить пункты меню.reg

Windows Registry Editor Version 5.00  [HKEY_CLASSES_ROOT\V83.ExternalProcessing\Shell\Захватить] @="Захватить" "Position"="Bottom" "Icon"="%SystemRoot%\\System32\\SHELL32.dll,47"  [HKEY_CLASSES_ROOT\V83.ExternalProcessing\Shell\Захватить\command] @="\"\\\\app1\\1C\\work\\Вложения\\Python_projects\\epf_Controll\\venv\\Scripts\\pythonw.exe\" \"\\\\app1\\1C\\work\\Вложения\\Python_projects\\epf_Controll\\main.pyw\" \"%1\" \"capture	\""  [HKEY_CLASSES_ROOT\V83.ExternalProcessing\Shell\Поместить] @="Поместить" "Position"="Bottom" "Icon"="%SystemRoot%\\System32\\SHELL32.dll,132"  [HKEY_CLASSES_ROOT\V83.ExternalProcessing\Shell\Поместить\command] @="\"\\\\app1\\1C\\work\\Вложения\\Python_projects\\epf_Controll\\venv\\Scripts\\pythonw.exe\" \"\\\\app1\\1C\\work\\Вложения\\Python_projects\\epf_Controll\\main.pyw\" \"%1\" \"put\""

Обращаю внимание!!! Мы используем 1С 8.3 в Вашем случае HKEY_CLASSES_ROOT\V83.ExternalProcessing может иметь иное значение!!!

[HKEY_CLASSES_ROOT\V83.ExternalProcessing\Shell\Захватить\command] @="\"\\\\app1\\1C\\work\\Вложения\\Python_projects\\epf_Controll\\venv\\Scripts\\pythonw.exe\" \"\\\\app1\\1C\\work\\Вложения\\Python_projects\\epf_Controll\\main.pyw\" \"%1\" \"capture\""  [HKEY_CLASSES_ROOT\V83.ExternalProcessing\Shell\Захватить\command] @="[Путь до запускатора python из папки venv] [Путь до скрипта - передается параметром в запускотор] [Путь к файлу который вы захватываете/отпускаете - передается первым параметром в скрипт] [Тип операции захватить/отпустить - передается вторым параметром в скрипт]"

Для работы с расширениями можно использовать filetypesman

После настройки данного файла вашим коллегам останется только стартануть его!

8. Настроим «main.pyw»

# -*- coding: utf-8 -*-  import pyperclip import datetime import win32con import win32api import logging import shutil import ctypes import json import sys import os CAPTURED_FILES_PATH = r"\app1\1C\work\Вложения\Python_projects\epf_Controll\ЗахваченныеФайлы.json" USERS_CONFIG        = r"\app1\1C\work\Вложения\Python_projects\epf_Controll\UsersConfig.json" OPERATION_LOG_FILE  = r"\app1\1C\work\Вложения\Python_projects\epf_Controll\log.txt" MAIN_DIR            = r"\app1\1C\work"  ...  def file_dir_is_ok(file):     file_lower = file.lower()     if not file_lower.startswith(MAIN_DIR.lower()):         msg = "Вы пытаетесь захватить обработку вне рабочего каталога!\r\rДоверенные пути:\r\r" \               "1.'\\app1\1C\work'\r\r" \               "2.'\\app1\1C\work\Вложения'\r\r" \               "3.'\\app1\1C\work\ExtForms'"         title = r'Неверная директория!'         warning(msg, title)         return False     return True    ...

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

Поменять подсказку msg.

9. Настроим «UsersConfig.json»

{     "IT-99907262": {             "ExtForms": "\\\\1C\\1Cv80\\krow42\\Extforms\\",             "Вложения": "\\\\1C\\1Cv80\\krow42\\Вложения\\"     } }  {     "[Имя компьютера]": {             "[Ключ 1]": [Путь 1],             "[Ключ 2]": [Путь 2]     } }

Ключ — это то, что будет пытаться найти прога в пути захватываемого файла, что бы выбрать один из путей по которому она будет сохранять захватываемый файл

Бонусом можете оформить в системе 1С монитор захваченных обработок. Например так:

Готово, можно пользоваться! Если понравится, можно провернуть данный трюк и с другими расширениями файлов, по аналогии…)

Допускаю, что некоторые моменты я упустил при описании порядка настройки, т. ч. пишите в комментарии чего не хватает, раскрою подробнее!

ссылка на оригинал статьи https://habr.com/ru/post/565446/