Переход на Boost-1.65.1 и баги, которые всплыли

в 18:12, , рубрики: boost, boost-1.65.1, boost::filesystem, boost::numpy, c++, Программирование

В прошлом году(уже почти целый год прошел) мы все–таки перешли на новую версию Boost-1.65.1, и под капотом вы найдете тройку багов boost-а, с которыми мы столкнулись. Еще важно упомянуть, что до этого у нас в ПО использовался boost -1.62.1, поскольку какие-то баги появились в boost ранее версии 1.65.1

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

Баг в boost::filesystem

Этот баг всплыл достаточно быстро. Тесты начали падать с “Access violation” при поиске полного пути к задаваемому имени файла. В функции делался вызов boost::filesystem::exist, и программа крашилась. Запустив еще несколько тестов, было замечено еще несколько аналогичных случаев, при этом во всех случаях вызов boost::filesystem::exist делался для глобальных переменных. Видимо, что-то поменялось во времени жизни переменных boost-та. Тикет для обнаруженного бага очень легко гуглится тикет бага в boost::filesystem::exist

Оказалось, что этот баг затесался в boost, начиная с версии 1.64. На самом деле проблема была в вызове make_permissions (используется в filesystem::exist). В 1.64 имплементация make_permissions была изменена, и теперь использовала глобальные переменные, а это значит, что когда делается попытка вызова filesystem::exist при инициализации глобальной переменной или объекта, глобальные переменные, используемые в make_permissions, могут быть еще не проинициализированы. Поэтому попытка доступа к несозданной переменной бросает исключение.

Обходной путь

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

В остальных случаях использовались синглтоны.

Баг в boost::python

В тестах, использующих boost::python, была обнаружена странная вещь. При выполнении тривиального вызова eval() для литерала (например, «40+2») все норм. А если переменные определить, а потом использовать в выражениях, то получаем сообщение о том, что в вычислениях используются неопределенные переменные(ERROR: [name] not defined). Для решения этой проблемы я потратила уже больше времени. Я не смогла найти тикет этой проблемы в баг трекере boost-а, поэтому пришлось попросить помощи команды этого компонента. Информация о баге была оперативно найдена на github-е.

Так случилось, что в имплементации eval объекты global и local не использовались. Пожелав Good luck в поиске фикса без перекомпиляции исходников либы, команда откланялась :)

Обходной путь

Но тут я вспомнила про release notes для boost-1.65.1 и там точно что-то было для boost::python.

Ура, выход есть! Баг был допущен при добавлении новой имплементации eval c поддержкой char const * аргумента, которая теперь вызывается в старой имплементации eval со string аргументом(Особо внимательные могли заметить вызов этой функции в коде по github-овской ссылке). И новая функция, как ожидалась, работает.

boost::numpy

Это самая нелюбимая моя часть. boost::python::numeric был удален и теперь как альтернатива появился boost::python::numpy. Но код, использовавший numeric, пришлось изрядно переделать, поскольку дело не только в переименовании неймспейсов, но и в имплементации объектов.

Помимо этого, в хедере boost-та была дезинформация, которая ввела меня в заблуждение.
Согласно комментарию в исходнике, вызов import_array() уже делается в numpy::initialize():

namespace boost { namespace python { namespace numpy {

/**
 *  @brief Initialize the Numpy C-API
 *
 *  This must be called before using anything in boost.numpy;
 *  It should probably be the first line inside BOOST_PYTHON_MODULE.
 *
 *  @internal This just calls the Numpy C-API functions "import_array()"
 *            and "import_ufunc()", and then calls
 *            dtype::register_scalar_converters().
 */
BOOST_NUMPY_DECL void initialize(bool register_scalar_converters=true);

}}} // namespace boost::python::numpy

Но на деле, как оказалось, import_array() необходим.

К тому же, были проблемы с тестированием изменений, поскольку куски кода с numpy (ранее с boost::python::numeric) вообще не были покрыты тестами, а сам код использовался еще и в другом компоненте. Поэтому проблемы выявлялись только при тестировании соответствующего компонента. Команда интеграции не обязана писать тесты для компонент, и данная ситуация была упущением самой команды. Ух и наслушалась я от них о том, что сломала их код. Но после того, как команда поворчала, они наконец-то покрыли свой код тестами. Однако обидка осталась (при следующей миграции, команда не хотела давать права доступа к своему компоненту моему коллеге, упоминая, что в прошлый раз, мы сломали им код. Саша, сорян! Но после трех дней переговоров они сдались).

Заключение

После проделанной работы могу отметить плюсы для себя, поскольку boost до этого использовала не очень часто(в основном std), поэтому из миграции можно подчеркнуть много нового. Забавно, но факт, после такого почему-то вы по дефолту становитесь для многих коллег “экспертом boost”, и, смиритесь, вам будут задавать вопросы по нему еще некоторое время.

Кстати последние годы многие компании начали активно избавляться от boost и заменять по возможности std библиотекой, либо чем-то еще в случае отсутствия каких-то возможностей в стандартной библиотеке. И мы тоже не остались в стороне. Процесс запущен, но не завершен, еще много работы.

Автор: AnROm

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js