Ну привет. Меня не зовут, я сам прихожу Пашечка.
Мне, во-первых, надоело в несколько чатов копировать свои истории, а во-вторых, я подумал, что они не то чтобы прям всем интересны 🌚
Поэтому мой шитпостинг будет теперь здесь.
В названии, картинке и описании канала небольшая игра слов: "let L tuple" можно прочитать почти как "little tuple" 🌚 И это немного тупо, да х)
Мне, во-первых, надоело в несколько чатов копировать свои истории, а во-вторых, я подумал, что они не то чтобы прям всем интересны 🌚
Поэтому мой шитпостинг будет теперь здесь.
Для тех, кто не знает ни меня, ни про меня:
Я - С++ программист, субъективно middle уровня, а объективно, надо выгнать меня мести улицы. В свободное время увлекаюсь попытками написать что-то на Rust'е. Что обидно, интересуюсь я Rust'ом примерно с 2014 года, но что-то более менее рабочее могу написать с 2019 только 🌚 Лоботряс и лентяй ибо.
На данный момент я работаю на предприятии ВПК РФ, всякие тренажёры разрабатываем, системы трёхмерной визуализации, симуляции, и прочее. По идее, деятельность моего отдела крайне близка к геймдеву, но лично я больше занимаюсь околосистемными вещами, многопоточностью, системой сборки, прикручиванием сторонних библиотек и их отладкой. Ну и у нас в отделе я проповедник плюсового стандарта, хотя и далеко не спец в его толковании)
Хочу свалить куда-то, где основным языком будет Rust, но пока не получается.
Я - С++ программист, субъективно middle уровня
На данный момент я работаю на предприятии ВПК РФ, всякие тренажёры разрабатываем, системы трёхмерной визуализации, симуляции, и прочее. По идее, деятельность моего отдела крайне близка к геймдеву, но лично я больше занимаюсь околосистемными вещами, многопоточностью, системой сборки, прикручиванием сторонних библиотек и их отладкой. Ну и у нас в отделе я проповедник плюсового стандарта, хотя и далеко не спец в его толковании)
Хочу свалить куда-то, где основным языком будет Rust, но пока не получается.
👍1
#truestory #cpp #jobbing
Ну а теперь история, ради которой этот канал был создан, и она про отладку сторонних библиотек.
Мы используем boost. Но мало того, что мы используем boost, мы используем старый boost, а именно версии 1.62, потому что он поставляется с Astra Linux 1.6. А Астра наша приоритетная целевая ОСь. (На самом деле под виндой у нас boost v1.64, но сути это не меняет)
И вот затащили мы некоторое время назад open-license-manager, в составе которого библиотека licensecc и генератор лицензий lccgen. И вот последний тоже зависит от boost'а. Ограничений на версию в CMakeLists.txt не установлено, а в документации проекта сказано использовать >= 1.57, и с нашей старой версией (напомню, что это 1.62) оно в принципе собирается, но только если отключить сборку тестов, но кому эти тесты нужны, правда?
Однако, при запуске, падает во время разбора аргументов командной строки (используется boost::program_options). Опытным путём выяснили, что если подкинуть версию boost'а 1.71, то падения пропадают. Кажется, даже тесты собираются, но это не точно, они же нинужны, поэтому я не перепроверял.
В принципе, мы генератор лицензий заказчикам не отдаём, поэтому собрали это дело только под окошками с новым boost'ом, забив на сертификацию, и всё бы ничего, но вот теперь потребовалось заиметь генератор и под Астру...
Ну а теперь история, ради которой этот канал был создан, и она про отладку сторонних библиотек.
Мы используем boost. Но мало того, что мы используем boost, мы используем старый boost, а именно версии 1.62, потому что он поставляется с Astra Linux 1.6. А Астра наша приоритетная целевая ОСь. (На самом деле под виндой у нас boost v1.64, но сути это не меняет)
И вот затащили мы некоторое время назад open-license-manager, в составе которого библиотека licensecc и генератор лицензий lccgen. И вот последний тоже зависит от boost'а. Ограничений на версию в CMakeLists.txt не установлено, а в документации проекта сказано использовать >= 1.57, и с нашей старой версией (напомню, что это 1.62) оно в принципе собирается, но только если отключить сборку тестов, но кому эти тесты нужны, правда?
Однако, при запуске, падает во время разбора аргументов командной строки (используется boost::program_options). Опытным путём выяснили, что если подкинуть версию boost'а 1.71, то падения пропадают. Кажется, даже тесты собираются, но это не точно, они же нинужны, поэтому я не перепроверял.
В принципе, мы генератор лицензий заказчикам не отдаём, поэтому собрали это дело только под окошками с новым boost'ом, забив на сертификацию, и всё бы ничего, но вот теперь потребовалось заиметь генератор и под Астру...
#jobbing
Маленькое отступление: почему Астра, это проблема при выборе зависимостей.
Astra Linux Special Edition, это операционная система для использования в критических инфраструктурах с повышенными требованиями к безопасности и защите данных. Она сертифицирована в ФСБ, ФСТЭК, и где-то там ещё, короче имеет все возможные отечественные сертификаты, что позволяет использовать её для работы с документами под грифом вплоть до "особой важности". Наше ПО тоже проходит такую же сертификацию (хотя скорее всего "облегчённую", не до конца уверен). А для того, чтобы получить сертифицированный продукт, необходимо использовать только сертифицированные инструменты и сторонние библиотеки. Теоретически можно самостоятельно собрать и новый boost, и новый cmake, и новое что угодно, но это тоже придётся сертифицировать. А сертификация стоит денег, и количество этих денег зависит от количества сертифицируемого кода. То есть, хочешь собирать больше зависимостей - плати больше денег. Поэтому мы сильно ограничены в инструментах.
Как-то я прикидывал, что нам будет стоить собрать по нормальному, по правильному, новые драйвера для AMD GPU, и вышло всё очень объёмно с тонной ненужных в рантайме зависимостей. Пришлось собирать кое-как, и при установке наших драйверов, по факту, портится система.
Маленькое отступление: почему Астра, это проблема при выборе зависимостей.
Astra Linux Special Edition, это операционная система для использования в критических инфраструктурах с повышенными требованиями к безопасности и защите данных. Она сертифицирована в ФСБ, ФСТЭК, и где-то там ещё, короче имеет все возможные отечественные сертификаты, что позволяет использовать её для работы с документами под грифом вплоть до "особой важности". Наше ПО тоже проходит такую же сертификацию (хотя скорее всего "облегчённую", не до конца уверен). А для того, чтобы получить сертифицированный продукт, необходимо использовать только сертифицированные инструменты и сторонние библиотеки. Теоретически можно самостоятельно собрать и новый boost, и новый cmake, и новое что угодно, но это тоже придётся сертифицировать. А сертификация стоит денег, и количество этих денег зависит от количества сертифицируемого кода. То есть, хочешь собирать больше зависимостей - плати больше денег. Поэтому мы сильно ограничены в инструментах.
Как-то я прикидывал, что нам будет стоить собрать по нормальному, по правильному, новые драйвера для AMD GPU, и вышло всё очень объёмно с тонной ненужных в рантайме зависимостей. Пришлось собирать кое-как, и при установке наших драйверов, по факту, портится система.
Немного туп(л,)
#truestory #cpp #jobbing Ну а теперь история, ради которой этот канал был создан, и она про отладку сторонних библиотек. Мы используем boost. Но мало того, что мы используем boost, мы используем старый boost, а именно версии 1.62, потому что он поставляется…
Возвращаемся к lccgen и boost.
Утилита
Падение происходит только если пользователь указал список фич, все остальные аргументы обрабатываются нормально. Поэтому, чтобы осознать, что не так, надо найти описание для списка фич:
Господи, как же это сделано неудобно! Функтор на функторе, блджад! Какой же clap офигенный на этом фоне!
Немного поменяю форматирование и уберу лишнее, оставив только суть:
К слову, это единственный параметр, который принимает значение
Утилита
lccgen
среди аргументов принимает список фич (features), которые добавляются в лицензию. То есть вы можете выдать лицензию только на ваш продукт, а можете разрабатывать дополнительные платные плагины, и формировать лицензию для некоторого набора этих плагинов. Эти фичи задаются через coma-separated list: -f Feat1,Feat2,Feat3
. И исключение выбрасывается здесь:po::store(
po::command_line_parser(opts) // Создаём парсер. opts - это вектор строк, куда собраны все ещё не обработанные аргументы
.options(project_desc) // Добавляем описание (для вывода хэлпа и привязки переданных аргументов к их описанию)
.run(), // Запускаем парсер
vm); // Перекладываем данные в variables_map
Падение происходит только если пользователь указал список фич, все остальные аргументы обрабатываются нормально. Поэтому, чтобы осознать, что не так, надо найти описание для списка фич:
// В каком-то хэдере
#define PARAM_FEATURE_NAMES "feature-names"
// Конкретно описание опций
po::options_description license_desc("license issue options");
// [...]
license_desc.add_options()
// [...]
(PARAM_FEATURE_NAMES ",f", po::value<boost::optional<std::string>>(),
"Feature names: comma separate list of project features to enable. if not specified will be taken as project "
"name.")
// [...]
;
Немного поменяю форматирование и уберу лишнее, оставив только суть:
license_desc.add_options()(
"feature-names,f" // Имя ключа
po::value<boost::optional<std::string>>(), // Тип данных аргумента
"<option-description>" // Описание
);
К слову, это единственный параметр, который принимает значение
optional<string>
, в остальных обычный string
. Почему авторы так сделали? Не знаю. Но лучше бы они так не делали.👍1
Немного туп(л,)
Возвращаемся к lccgen и boost. Утилита lccgen среди аргументов принимает список фич (features), которые добавляются в лицензию. То есть вы можете выдать лицензию только на ваш продукт, а можете разрабатывать дополнительные платные плагины, и формировать лицензию…
Дальше, чтобы разобраться основательнее, в чём же разница, я собрал lccgen с обеими версиями boost'а, и начал в отладчике ходить по ним параллельно.
Если кратко, внутри проблемного вызова
1. Перебираем все даденные опции
2. Создаём в
3. Парсим в соответствии с семантикой, записанной в описании опции и перекладываем в созданный в
И вот на последнем пункте появляются расхождения.
В boost'е заготовлено несколько шаблонных валидаторов. И вот старая версия в ходе работы попадает в
Конкретная специализация шаблона для старого boost'а:
В новой версии выполнение уходит на второй круг валидации (в более "тонкую" и конкретную реализацию), где происходит финальная корректная запись.
А в старой версии используется
1. Создаётся
2. Создаётся
3. Вызывается
В этом операторе происходит следующее:
1. Если первый символ в потоке пробел, то записываем значение.
2. Если первые два символа в потоке "--", то присваиваем None.
3. В противном случае - устанавливаем failbit.
А failbit, это значит, что запись не удалась. То есть
Если кратко, внутри проблемного вызова
ps::store()
происходит следующее:1. Перебираем все даденные опции
2. Создаём в
vm
пустой элемент (по факту boost::any
с NULL значением)3. Парсим в соответствии с семантикой, записанной в описании опции и перекладываем в созданный в
vm
элементИ вот на последнем пункте появляются расхождения.
В boost'е заготовлено несколько шаблонных валидаторов. И вот старая версия в ходе работы попадает в
template<class T, class charT>А новая в:
void validate(boost::any& v,
const std::vector< std::basic_string<charT> >& xs,
T*, long)
template<class T, class charT>В описании к первой указано, что это "специальная функция для компиляторов, которые не поддерживают partial template ordering". Нюанс в том, что компилятор используется один и тот же 🌚
void validate(boost::any& v,
const std::vector<std::basic_string<charT> >& s,
boost::optional<T>*,
int)
Конкретная специализация шаблона для старого boost'а:
validate<boost::optional<std::string>, char>
, а для нового: validate<std::string, char>
.В новой версии выполнение уходит на второй круг валидации (в более "тонкую" и конкретную реализацию), где происходит финальная корректная запись.
А в старой версии используется
lexical_cast
, и вот как оно выглядит в конкретной специализированной версии:boost::optional<std::string> lexical_cast(const std::string& arg)Функция
{
auto result = boost::optional<std::string>();
if (!boost::conversion::detail::try_lexical_convert(arg, result)) {
boost::conversion::detail::throw_bad_cast<std::string, boost::optional<std::string>>();
}
return result;
}
try_lexical_convert
это сложная шаблонная магия, где вызывается другая сложная шаблонная магия, где всё сводится к:i_interpreter_type i_interpreter;
// Disabling ADL, by directly specifying operators.
if (!(i_interpreter.operator <<(arg)))
return false;
o_interpreter_type out(i_interpreter.cbegin(), i_interpreter.cend());
// Disabling ADL, by directly specifying operators.
if(!(out.operator >>(result)))
return false;
return true;
i_interpreter_type
и o_interpreter_type
это урезанные istream
и ostream
. По факту здесь:1. Создаётся
basic_streambuf
, который инициализируется нашей исходной строкой2. Создаётся
std::basic_istream
, инициализированный буфером, созданным на 1 шаге3. Вызывается
operator>>(std::basic_istream&, boost::optional<T>&)
, где T
- std::string
. Не знаю, важно или нет, но для std::string
нет явной специализации.В этом операторе происходит следующее:
1. Если первый символ в потоке пробел, то записываем значение.
2. Если первые два символа в потоке "--", то присваиваем None.
3. В противном случае - устанавливаем failbit.
А failbit, это значит, что запись не удалась. То есть
lexical_cast
выполнился с ошибкой, что приводит к throw bad_lexical_cast
, которое перехватывается и выбрасывается error_with_option_name
, который опять перехватывается, к нему добавляется контекстная информация, и происходит rethrow. По идее оно бы должно тоже где-то перехватиться, но по факту у меня этого почему-то не происходит. Да и не важно, на самом деле, почему.
Немного туп(л,)
Дальше, чтобы разобраться основательнее, в чём же разница, я собрал lccgen с обеими версиями boost'а, и начал в отладчике ходить по ним параллельно. Если кратко, внутри проблемного вызова ps::store() происходит следующее: 1. Перебираем все даденные опции 2.…
Итак. Что делать?
На вскидку вариантов два:
1. Сделать так, чтобы строка с фичами всегда начиналась с пробела. Я попробовал указывать
2. Перелопатить код lccgen, чтобы поменять
3. Пропатчить boost, убрав эту проверку
4. Обновить boost
Наблюдательный читатель обратит внимание, что пункты 3 и 4 out-of-range, поэтому их отметаем. Тем более, что 4 не реализуем по причине, которую я расписывал ранее, а 3 делать не стоит, ибо велик риск что-то сломать.
Править код lccgen?.. Нуууууу, мооожно конечно, но мне лень. Остаётся первое, сделать так, чтобы пробел был всегда.
И сделать это очень просто, благо что для запуска генератора используется sh/bat-скрипт, потому что аргументов ну очень много писать вручную (фич около 20 штук), плюс те, кто будет этим пользоваться, не очень-то умеют в консоль.
За сим история завершена. Два дня отладки внутри boost'а, тонна текста на этом канале (да и сам канал), и всего один дополнительный пробел в скрипте запуска lccgen.
На вскидку вариантов два:
1. Сделать так, чтобы строка с фичами всегда начиналась с пробела. Я попробовал указывать
-f " Feat1,Feat2"
(обратите внимание на пробел в кавычках) и это реально работает. Программа работает как ожидается.2. Перелопатить код lccgen, чтобы поменять
optional<string>
на string
с пустым значением по умолчанию. Теоретически должно быть не сложно.3. Пропатчить boost, убрав эту проверку
4. Обновить boost
Наблюдательный читатель обратит внимание, что пункты 3 и 4 out-of-range, поэтому их отметаем. Тем более, что 4 не реализуем по причине, которую я расписывал ранее, а 3 делать не стоит, ибо велик риск что-то сломать.
Править код lccgen?.. Нуууууу, мооожно конечно, но мне лень. Остаётся первое, сделать так, чтобы пробел был всегда.
И сделать это очень просто, благо что для запуска генератора используется sh/bat-скрипт, потому что аргументов ну очень много писать вручную (фич около 20 штук), плюс те, кто будет этим пользоваться, не очень-то умеют в консоль.
За сим история завершена. Два дня отладки внутри boost'а, тонна текста на этом канале (да и сам канал), и всего один дополнительный пробел в скрипте запуска lccgen.
#irl
Вот сидишь ты такой вне дома. Дома окажешься хз когда. На телефоне 8% зарядки осталось. Но у тебя же есть powerbank! Вот только кабель зарядный ты дома забыл 🥲
Вот сидишь ты такой вне дома. Дома окажешься хз когда. На телефоне 8% зарядки осталось. Но у тебя же есть powerbank! Вот только кабель зарядный ты дома забыл 🥲
#haha #rust
Я тут между делом пилю свою плагиновую систему на расте. Главная её идея - safety. То есть, тот же (самый популярный) libloading - полностью unsafe, и от этого я хочу уйти. Если вкратце, уход от unsafe достигается за счёт добавления в плагин дополнительных метаданных и их проверке при загрузке. Ну как "достигается". Будет "достигаться", если я это дело допилю, потому что оно пока очень unsound :)Ломаю safe гарантию полностью!
Но суть вот в чём. Изначально я дал этому проекту название "plunger", потому что созвучно с "plugin", а я люблю созвучия и игру слов. Глянул в переводчик, он даёт перевод "поршень", меня устроило. При этом внутри я использовал всё тот же libloading, ибо зачем выдумывать лишнее, если база уже есть? А поддержку MacOS можно было бы и позже законтрибьютить. Но тут я столкнулся с нюансом, который мне не нравится совсем. И было принято решение запилить свою реализацию, благо это не rocket science. А для реализации надо придумать название. И пошёл я гуглить, что бы такое похожее на "поршень" придумать. И выяснил, что "plunger", с точки зрения англо-говорящего человека, это всё-таки не столько "поршень", сколько "вантуз". Не очень удачное имечко, для библиотеки 🌚
Штош, пойду искать что-то другое. Если кто предложит варианты, я только за 😊
P.S. К слову, может я ещё себя пересилю и не буду делать велосипед, а всё-таки обойдусь с помощью libloading, но имя проекту менять точно надо 😅
Я тут между делом пилю свою плагиновую систему на расте. Главная её идея - safety. То есть, тот же (самый популярный) libloading - полностью unsafe, и от этого я хочу уйти. Если вкратце, уход от unsafe достигается за счёт добавления в плагин дополнительных метаданных и их проверке при загрузке. Ну как "достигается". Будет "достигаться", если я это дело допилю, потому что оно пока очень unsound :)
Штош, пойду искать что-то другое. Если кто предложит варианты, я только за 😊
P.S. К слову, может я ещё себя пересилю и не буду делать велосипед, а всё-таки обойдусь с помощью libloading, но имя проекту менять точно надо 😅