В прошлом году(уже почти целый год прошел) мы все–таки перешли на новую версию 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 библиотекой, либо чем-то еще в случае отсутствия каких-то возможностей в стандартной библиотеке. И мы тоже не остались в стороне. Процесс запущен, но не завершен, еще много работы.

Комментарии (8)


  1. mst_72
    20.01.2019 22:05
    +3

    Боже ты мой. Когда-то давным давно все старались делать на STL. При этом апологеты Boosta вопили, какие все тупые идиоты, что с STL не слазят. Апологеты победили. Все стали юзать буст… А теперь "внезапно" пошел откат? Здравый смысл одержал победу на дизайном? Не верю :)


    1. AnROm Автор
      20.01.2019 22:16
      +5

      Просто раньше в стандартной библиотеке не было многих очень полезных вещей и спасательным кругом был именно boost


    1. encyclopedist
      21.01.2019 03:00
      +1

      Расцвет буста не случайно пришёлся на период затянувшейся паузы между C++98 и C++11. С тех пор многое самое нужное было добавлено в стандартную библиотеку и с тех пор процесс стандартизации идет гораздо плавнее.


  1. rg_software
    21.01.2019 01:09

    А обновлялись ради какой функциональности?


    1. AnROm Автор
      21.01.2019 10:02

      • В новом бусте были исправлены некоторые баги, по просьбам трудящихся, это тоже учитывалось(помню точно про запросы для asio и geometry).
      • Оптимизации в имплементациях boost объектов.
      • У нас работает два бэк-офиса и, собственно, фронт офис. Есть код, который используется в проектах обоих бэк-офисов. Один из офисов любит, чтоб было «стильно, модно, молодежно». Поэтому переход на более свежую версию буста можно рассматривать и как политические уступки.


      1. rg_software
        21.01.2019 11:44

        Хороший у вас разброс — от filesystem до asio и geometry :)
        Вы, конечно, понимаете, к чему я клоню: люди неидеальны, софт неидеален. Всегда есть шансы багов. Переезд — это время, а стало быть, деньги. «Один из офисов» во сути заплатил за свою хотелку. Стоило оно того — ну, решать руководителю, конечно.


        1. AnROm Автор
          21.01.2019 11:57

          Хороший у вас разброс — от filesystem до asio и geometry :)

          Пишем софт для сложного медицинского оборудования, поэтому используем много всего ))


  1. pvsur
    21.01.2019 17:14

    Пользуюсь 1.68, пока не натыкался… Но и в boost::filesystem::exist глобальные переменные не проверял. Обычно глобальная конфигурация в синглтоне упакована.

    Хотя странные падения с бэктрейсом, ведущим в глубокие потроха буста, встречаются иногда… Бывает, после простой перекомпиляции с косметическим изменением кода они пропадают :)