[ROOT 6.32] Vector of Pointers & Python

Describe the bug

With ROOT 6.32, vector<TFormula*> seems to trigger a deallocation issue with Python.

Setup

ROOT v6.32.08
Built for linuxx8664gcc on Jan 24 2025, 09:37:12
From tags/v6-32-08@v6-32-08
With c++ (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0

ROOT built from source:

$ root-config --features
cxx17 asimage builtin_clang builtin_cling builtin_fftw3 builtin_gsl builtin_gtest builtin_llvm builtin_nlohmannjson dataframe fftw3 mathmore pyroot roofit runtime_cxxmodules shared soversion sqlite x11 xml

To Reproduce

Please consider the following MWE: Files · raw_ptr · Uranie CEA / Prerequisites / Prerequisites Detection / Mini ROOT Library · GitLab

After loading ROOT, here are the steps to reproduce the error on Linux:

$ git clone -b raw_ptr https://gitlab.com/uranie-cea/prerequisites/prerequisites_detection/mini-root-library.git
$ mkdir build
$ cd build/
$ cmake -DCMAKE_INSTALL_PREFIX=install ../
$ cmake --build . --target install -j $(nproc)
$ ctest -V -R "builder_python"
[...]
4: ROOT: 6.32.08
4: trend: x
4:  *** Break *** segmentation violation
[...]
4: #7  0x00007d0d7d991100 in MiniLibrary::TGPBuilder::~TGPBuilder() () from /.../mini-root-library/build/install/lib/libTGPBuilder.so
4: #8  0x00007d0d7d992aca in ROOT::delete_MiniLibrarycLcLTGPBuilder(void*) () from /.../mini-root-library/build/install/lib/libTGPBuilder.so
4: #9  0x00007d0d6ade7835 in CPyCppyy::op_dealloc_nofree(CPyCppyy::CPPInstance*) () from /.../root/lib/libcppyy.so

Here are the definition & implementation of the class:

    class TGPBuilder {
    private:
        std::vector<TFormula*> _trends;
    public:
        TGPBuilder();
        ~TGPBuilder();
        std::vector<TFormula*> get_trends();
    };
MiniLibrary::TGPBuilder::TGPBuilder() {
    _trends.push_back(new TFormula("f", "x"));
}

MiniLibrary::TGPBuilder::~TGPBuilder() {
    for(auto& trend: _trends)
        delete trend;
    _trends.clear();
}

std::vector<TFormula*> MiniLibrary::TGPBuilder::get_trends() {
    return _trends;
}

And here is the test: test_builder.py · raw_ptr · Uranie CEA / Prerequisites / Prerequisites Detection / Mini ROOT Library · GitLab

import ROOT

print('ROOT:', ROOT.gROOT.GetVersion())
builder = ROOT.MiniLibrary.TGPBuilder()

for trend in builder.get_trends():
    print('trend:', trend.GetExpFormula())

Expected behavior

The test seems to pass with ROOT 6.34:

4: ROOT: 6.34.04
4: Print:
4: 	trend: x
4: Modification
4: Print:
4: 	trend: 
1/1 Test #4: builder_python ...................   Passed    0.70 sec

Solutions?

Which solutions do you recommand?

Using smart pointers seems to solve the issue: Files · shared_ptr · Uranie CEA / Prerequisites / Prerequisites Detection / Mini ROOT Library · GitLab

https://gitlab.com/uranie-cea/prerequisites/prerequisites_detection/mini-root-library/-/blob/shared_ptr/modeler/inc/TGPBuilder.h?ref_type=tags:

    class TGPBuilder {
    private:
        std::vector<std::shared_ptr<TFormula>> _trends;
    public:
        TGPBuilder();
        const std::vector<std::shared_ptr<TFormula>>& get_trends() const;
    };

https://gitlab.com/uranie-cea/prerequisites/prerequisites_detection/mini-root-library/-/blob/shared_ptr/modeler/src/TGPBuilder.cpp?ref_type=tags:

const std::vector<std::shared_ptr<TFormula>>& MiniLibrary::TGPBuilder::get_trends() const {
    return _trends;
}
$ git switch -d shared_ptr
[...]
$ ctest -V -R "builder_python"
[...]
4: ROOT: 6.32.08
4: Print:
4: 	trend: x
4: Modification
4: Print:
4: 	trend: 
1/1 Test #4: builder_python ...................   Passed    0.70 sec

Dear @Salomon,

let me add in the loop @vpadulan and @jonas.

Cheers,
Monica

1 Like