ROOT can't find an STL base class (?)

Dear ROOT community,

I’m on an experiment that uses the root library as a framework for data file I/O (among other things). We have a base class for global tracks that can be used throughout the experiment analysis. However, it has fallen prey to a somewhat suspect programming practice, as I shall now show:

#ifndef RBTRACK_H
#define RBTRACK_H

#include “TObject.h”
#include

class RBTrackPoint;

class RBTrack : public std::vector, public TObject
{
public:
typedef std::vector::iterator iterator;
typedef std::vector::const_iterator const_iterator;
public:
RBTrack();
~RBTrack();

const RBTrackPoint& Point(int i) const;
int FindZ(double z, int& i1, int& i2) const;
int GetXY(double z, double* x, double* y) const;
int GetPatZ(double z, double* pxyz) const;
int Id() const {return fId;}

void SetId(int id) { fId = id;}

private:
int fId;

ClassDef(RBTrack,1) // Track base class
};

#endif // RBTRACK_H

As you may have noticed, this inherits from both a TObject AND an STL vector to a class that also derives from TObject. When the author tries to use this class to read in the object later from a TClonesArray, horrible things happen…
|=======================================================================
evt.Reco()
Error in TStreamerInfo::BuildOld: Could not find STL base class: vector for RBTrack

*** Break *** segmentation violation
Generating stack trace…
/usr/bin/addr2line: edm_dump: No such file or directory
/usr/bin/addr2line: edm_dump: No such file or directory
0x41726ca8 in from /lib/i686/libc.so.6
0x402c7a1b in int TStreamerInfo::ReadBuffer<char**>(TBuffer&, char** const&, int, int, int, int) + 0x12db from /mipp/root/lib/libCore.so.4.02
0x402acb6d in TClass::Streamer(void*, TBuffer&) + 0x1dd from /mipp/root/lib/libCore.so.4.02
0x402aca21 in TClass::Streamer(void*, TBuffer&) + 0x91 from /mipp/root/lib/libCore.so.4.02
0x401f2d5b in TBuffer::StreamObject(void*, TClass const*) + 0x2b from /mipp/root/lib/libCore.so.4.02
0x402872ae in TEmulatedCollectionProxy::ReadItems(int, TBuffer&) + 0x1ce from /mipp/root/lib/libCore.so.4.02
0x40287bbd in TEmulatedCollectionProxy::Streamer(TBuffer&) + 0xcd from /mipp/root/lib/libCore.so.4.02
0x40284c98 in TCollectionStreamer::Streamer(TBuffer&, void*, int) + 0x48 from /mipp/root/lib/libCore.so.4.02
0x40284e86 in TCollectionClassStreamer::operator()(TBuffer&, void*) + 0x36 from /mipp/root/lib/libCore.so.4.02
0x402acc2c in TClass::Streamer(void*, TBuffer&) + 0x29c from /mipp/root/lib/libCore.so.4.02
0x402aca21 in TClass::Streamer(void*, TBuffer&) + 0x91 from /mipp/root/lib/libCore.so.4.02
0x401f0796 in TBuffer::ReadFastArray(void*, TClass const*, int, TMemberStreamer*) + 0x86 from /mipp/root/lib/libCore.so.4.02
0x402c9def in int TStreamerInfo::ReadBuffer<char**>(TBuffer&, char** const&, int, int, int, int) + 0x36af from /mipp/root/lib/libCore.so.4.02
0x402c80e5 in int TStreamerInfo::ReadBuffer<char**>(TBuffer&, char** const&, int, int, int, int) + 0x19a5 from /mipp/root/lib/libCore.so.4.02
0x402c6731 in TStreamerInfo::ReadBufferClones(TBuffer&, TClonesArray*, int, int, int) + 0x61 from /mipp/root/lib/libCore.so.4.02
0x40281820 in TClonesArray::Streamer(TBuffer&) + 0x260 from /mipp/root/lib/libCore.so.4.02
0x402acaee in TClass::Streamer(void*, TBuffer&) + 0x15e from /mipp/root/lib/libCore.so.4.02
0x401f1f8d in TBuffer::ReadObjectAny(TClass const*) + 0x1cd from /mipp/root/lib/libCore.so.4.02
0x40283e87 in TBuffer& operator>>(TBuffer&, TObject*&) + 0x37 from /mipp/root/lib/libCore.so.4.02
0x40293d36 in TList::Streamer(TBuffer&) + 0x316 from /mipp/root/lib/libCore.so.4.02
0x402acaee in TClass::Streamer(void*, TBuffer&) + 0x15e from /mipp/root/lib/libCore.so.4.02
0x401f1f8d in TBuffer::ReadObjectAny(TClass const*) + 0x1cd from /mipp/root/lib/libCore.so.4.02
0x401f085b in TBuffer::ReadFastArray(void**, TClass const*, int, bool, TMemberStreamer*) + 0xab from /mipp/root/lib/libCore.so.4.02
0x402c816f in int TStreamerInfo::ReadBuffer<char**>(TBuffer&, char** const&, int, int, int, int) + 0x1a2f from /mipp/root/lib/libCore.so.4.02
0x402ac59c in TClass::ReadBuffer(TBuffer&, void*) + 0x19c from /mipp/root/lib/libCore.so.4.02
0x4041c931 in TFolder::Streamer(TBuffer&) + 0x3f from /mipp/root/lib/libCore.so.4.02
0x402acaee in TClass::Streamer(void*, TBuffer&) + 0x15e from /mipp/root/lib/libCore.so.4.02
0x401f1f8d in TBuffer::ReadObjectAny(TClass const*) + 0x1cd from /mipp/root/lib/libCore.so.4.02
0x40283e87 in TBuffer& operator>>(TBuffer&, TObject*&) + 0x37 from /mipp/root/lib/libCore.so.4.02
0x40293d36 in TList::Streamer(TBuffer&) + 0x316 from /mipp/root/lib/libCore.so.4.02
0x402acaee in TClass::Streamer(void*, TBuffer&) + 0x15e from /mipp/root/lib/libCore.so.4.02
0x401f1f8d in TBuffer::ReadObjectAny(TClass const*) + 0x1cd from /mipp/root/lib/libCore.so.4.02
0x401f085b in TBuffer::ReadFastArray(void**, TClass const*, int, bool, TMemberStreamer*) + 0xab from /mipp/root/lib/libCore.so.4.02
0x402c816f in int TStreamerInfo::ReadBuffer<char**>(TBuffer&, char** const&, int, int, int, int) + 0x1a2f from /mipp/root/lib/libCore.so.4.02
0x402ac59c in TClass::ReadBuffer(TBuffer&, void*) + 0x19c from /mipp/root/lib/libCore.so.4.02
0x4041c931 in TFolder::Streamer(TBuffer&) + 0x3f from /mipp/root/lib/libCore.so.4.02
0x402acaee in TClass::Streamer(void*, TBuffer&) + 0x15e from /mipp/root/lib/libCore.so.4.02
0x401f1f8d in TBuffer::ReadObjectAny(TClass const*) + 0x1cd from /mipp/root/lib/libCore.so.4.02
0x40283e87 in TBuffer& operator>>(TBuffer&, TObject*&) + 0x37 from /mipp/root/lib/libCore.so.4.02
0x40293d36 in TList::Streamer(TBuffer&) + 0x316 from /mipp/root/lib/libCore.so.4.02
0x402acaee in TClass::Streamer(void*, TBuffer&) + 0x15e from /mipp/root/lib/libCore.so.4.02
0x401f1f8d in TBuffer::ReadObjectAny(TClass const*) + 0x1cd from /mipp/root/lib/libCore.so.4.02
0x401f085b in TBuffer::ReadFastArray(void**, TClass const*, int, bool, TMemberStreamer*) + 0xab from /mipp/root/lib/libCore.so.4.02
0x402c816f in int TStreamerInfo::ReadBuffer<char**>(TBuffer&, char** const&, int, int, int, int) + 0x1a2f from /mipp/root/lib/libCore.so.4.02
0x40fe295e in TBranchElement::ReadLeaves(TBuffer&) + 0x14e from /mipp/root/lib/libTree.so.4.02
0x40fd8127 in TBranch::GetEntry(long long, int) + 0x167 from /mipp/root/lib/libTree.so.4.02
0x40fe1990 in TBranchElement::GetEntry(long long, int) + 0x220 from /mipp/root/lib/libTree.so.4.02
0x40fe19d3 in TBranchElement::GetEntry(long long, int) + 0x263 from /mipp/root/lib/libTree.so.4.02
0x4001ed69 in IoEventHandle::Load(int) const at /mipp/packages/IoModules/HEAD/IoEventHandle.cxx:219 from /mipp/releases/development/lib/Linux2-GCC/libIoModules.so
0x400ca1b7 in EDMEventHandle::Bucket(EDMEventHandle::BranchID_t) const at /home2/jsh/mipprels/EventDataModel/EDMEventHandle.cxx:112 from ./lib/Linux2-GCC/libEventDataModel.so
0x400ca2ff in EDMEventHandle::Reco() at /home2/jsh/mipprels/EventDataModel/EDMEventHandle.cxx:134 from ./lib/Linux2-GCC/libEventDataModel.so
0x0804987a in main + 0x612 from edm_dump
0x41713c57 in __libc_start_main + 0xc7 from /lib/i686/libc.so.6
0x080491a1 in _Unwind_Resume + 0x4d from edm_dump
Aborted

The main thing is that ROOT can’t find the info on the STL vector base class. Why would something like this happen? Do we need to pass extra information to ROOT? Does ROOT need to be rebuilt? Should we throw eggs at the person who tried to double inherit from an STL container?

Many thanks in advance,
Joshua Hendrickson

Hi,

The only way I can reproduce this problem is when reading a file without the shared library implementing RBTrack. This is indeed a problem and should be fixed (we will fix it).
In the meantime, it should work if you simply load the appropriate library.

Cheers,
Philippe.

Thanks for the reply Philippe.

I’ve done some more looking in to the problem, and I’ve found some interesting things out…

This problem occured because I tried to circumvent a different issue happening in our code. Wwe have two functions for inserting objects into the root file: one for fixed size data structures, and one for variable sized data structures. The one for variable sized data structures caused an error because (I believe) the fClassInfo member of TClass wasn’t set properly. This is not because of a missing dictionary, because other classes that get dicitonary warnings don’t seem to have this problem.

“Error in TBuffer::ReadObject: trying to read an emulated class (RBTrack) to store in a compiled pointer (TObject)”

This is what we get when we try the variable sized Put. What’s curious is that no one else in our collaboration, to my knowledge, has had these problems.

I’ll cut and paste the code (Put() function and the other functions it calls) that does the putting into the object arrays. I’ve highlighted what I believe are the important parts of the functions in bold. Maybe someone will notice something out of place:

if (opt & kFixedSizeObject) {
TClonesArray *c = this->GetClonesArray(fullPath.c_str());
if (c == 0) {
// No collection of this type of object. Make a new collection
if ((c = this->NewClonesArray(path, collectName.c_str())) == 0) {
std::cerr << “EDMDataBucket: no folder " << path << " found”
<< std::endl;
throw EDMException(FILE,LINE,EDMX_NO_FOLDER_FOUND);
}
}

// Create new object of type T in event store. The "new with
// placement" is a signature of the TClonesArray container
assert(c!=0); // By this time we must have a home for the object
<B>indx = c->GetLast()+1;
T* t = new ((*c)[indx]) T(obj);
return t; // Add OK</B>

}

if (opt & kVariableSizeObject) {
TObjArray* c = this->GetObjArray(fullPath.c_str());
if (c == 0) {
// No collection of this type of object. Make a new collection
if ((c = this->NewObjArray(path, collectName.c_str())) == 0) {
std::cerr << “EDMDataBucket: no folder " << path << " found”
<< std::endl;
throw EDMException(FILE,LINE,EDMX_NO_FOLDER_FOUND);
}
}

// Create new object of type T in event store.
assert(c!=0); // By this time we must have a home for the object

T* t = new T(obj);
c->AddLast(t);
return t; // Add OK

}

//…
///
/// Return the object array stored at ‘path’
///
TObjArray* EDMDataBucket::GetObjArray(const char* path) const
{
// Allocate arrays where we copy split path, they will be no longer
// than path itself
int len = strlen(path) + 1;
char pathto[len];
char objarray[len];

Split(path, pathto, objarray);

TFolder* f = this->GetFolder(pathto);
if (f==0) return 0;

TObject* obj = f->FindObject(objarray);
TObjArray* oarray = dynamic_cast<TObjArray*>(obj);

return oarray;
}

//…
///
/// Return the object array stored at ‘path’
///
TFolder* EDMDataBucket::GetFolder(const char* path) const
{

// The top folder in the directory path can be referred to as “” or “.“
if (strcmp(””, path) == 0) return fTopFolder;
if (strcmp(".", path) == 0) return fTopFolder;
if (strcmp("/", path) == 0) return fTopFolder;

// Allocate arrays where we copy split path, they will be no longer
// than path itself
int len = strlen(path) + 1;
char pathto[len];
char folder[len];

Split(path, pathto, folder);

// Deeper in – make search
TFolder* f1 = this->GetFolder(pathto);
if (f1==0) return 0;

TFolder* f2 = dynamic_cast<TFolder*>(f1->FindObjectAny(folder));
return f2;
}

//----------------------------------------------------------------------
///
/// Split a string like /a/long/directory/path/to/object into
/// fullpath=/a/long/directory/path/to and obj=object
///
void EDMDataBucket::Split(const char* fullpath, char* pathto, char* obj)

The kFixedSizeObject branch caused the massive seg fault, and the kVariableSize object caused the emulated class error. From what you see here, should these errors have happened?

Thanks again for the reply,
Joshua Hendrickson

Oh dear…

As it turns out, what you said above made sense after all Philippe…the program I was running does not link the RecoBase library, which contains RBTrack. When I linked the library, everything worked fine.

I apologize for the false alarm. However, I still feel like sending an email about trying to inherit from an STL class. Thanks for your consideration.

Joshua Hendrickson