Objects are not filled when reading TBranch with GetEntry()

Hi all.

Here is some example code that demonstrates my problem. Could someone tell me what is going wrong? I have been reading and writing to root trees this way for a while (or so I think) without issue. Now it is suddenly not working. When I would use TTree::StartViewer() I could see that the branches were filled with data. However when I tried to read them out in the code the objects were only populated with the default values, making me think they weren’t being read out at all. I wrote the code below to test if I could write and read objects the way I thought I could (the way I have been doing) in a simpler setting. However this test code has a segmentation violation.

I created the dictionary by compiling the linkdef file with the header and source (empty in this case) for the RootTestData in the typical way.

Could somebody take a look and tell me what is going wrong? I have so much trouble with ROOT I/O. Thanks very much. (P.S. I have tried with Branch::GetEntry() and Branch::GetEvent() but neither have worked. What’s the difference?)

Thanks

RootTestData.hh:

[code]class RootTestData
{
public:

RootTestData()
: a(-1)
{
	// initialize a;
}

int a;		

};[/code]

RootTest.hh:

[code]class RootTest
{
public:
void go()
{

	// write some data.
	{
		TFile* file = new TFile("rootTest.root", "RECREATE", "rootTest");
		TTree* tree = new TTree( "tree", "tree");
		RootTestData* rtd = new RootTestData();
		tree->Branch("a", rtd);
	
		for(size_t i = 0; i < 1000; ++i )
		{
			rtd->a = i;
			tree->Fill();
		
		}
	
		tree->Print();
		file->Write();
		file->Close();
	}

	// read the data out.
	{
		TFile f("rootTest.root");
		RootTestData* data = new RootTestData();
		TTree* t = (TTree*)f.Get("tree");

		TBranch* branch = t->GetBranch("a");
		branch->SetAddress(data);
		
		for(size_t i = 1; i < 1000; ++i )
		{
			branch->GetEvent(i);
			std::cout << "data.a = " << data->a << "\n";
		}
	}
}

};[/code]

my root link def file for the dictionary:

[code]#ifdef MAKECINT

#pragma link off all typedefs;
#pragma link off all globals;
#pragma link off all functions;
#pragma link off all classes;

#pragma link C++ class RootTestData+;

#endif /* MAKECINT */[/code]

[quote] *** Break *** segmentation violation

===========================================================
There was a crash.
This is the entire stack trace of all threads:

Thread 1 (process 16701):
#0 0x00007fff815b0f6c in wait4 ()
#1 0x00007fff815c551a in system ()
#2 0x00000001028905d5 in TUnixSystem::StackTrace ()
#3 0x000000010288df5a in TUnixSystem::DispatchSignals ()
#4
#5 0x000000010380d1ec in TBufferFile::ReadInt ()
#6 0x00000001038bc277 in TStreamerInfo::ReadBuffer<char**> ()
#7 0x0000000104403321 in TBranchElement::ReadLeaves ()
#8 0x00000001043f69ed in TBranch::GetEntry ()
#9 0x00000001044001a4 in TBranchElement::GetEntry ()
#10 0x0000000104400165 in TBranchElement::GetEntry ()
#11 0x0000000100003221 in RootTest::go (this=<value temporarily unavailable, due to optimizations>) at RootTest.hh:46
#12 0x00000001000021c1 in main () at dualImager.cc:50

The lines below might hint at the cause of the crash.
If they do not help you then please submit a bug report at
root.cern.ch/bugs. Please post the ENTIRE stack trace
from above as an attachment in addition to anything else
that might help us fixing this issue.

#5 0x000000010380d1ec in TBufferFile::ReadInt ()
#6 0x00000001038bc277 in TStreamerInfo::ReadBuffer<char**> ()
#7 0x0000000104403321 in TBranchElement::ReadLeaves ()
#8 0x00000001043f69ed in TBranch::GetEntry ()
#9 0x00000001044001a4 in TBranchElement::GetEntry ()
#10 0x0000000104400165 in TBranchElement::GetEntry ()
#11 0x0000000100003221 in RootTest::go (this=<value temporarily unavailable, due to optimizations>) at RootTest.hh:46
#12 0x00000001000021c1 in main () at dualImager.cc:50

[/quote]

If I take out the custom constructor that initializes a to the value of -1 and recreate the dictionary it no longer segments. Instead it outputs:

[quote]…
data.a = 96205536
data.a = 96205536
data.a = 96205536
data.a = 96205536
…[/quote]

which looks like a has just been initialized to a default garbage value. What is the difference here?

If I try to draw a histogram of the integer a with the TTree Viewer it fails and says:

[quote]TCanvas::MakeDefCanvas: created default TCanvas with name c1
Warning in TSelectorDraw::ProcessFillObject: Not implemented for RootTestData[/quote]

Any clue what is going on?

So apparently if you have data like an integer stored in a tree, you are good to go if you read it out by passing in a pointer to the data you want filled:

double dog; TBranch* branch = tree->GetBranch("my_int"); branch->SetAddress(&dog); branch->GetEntry(0); // fills dog

however if you have an actual object you would like to read out, it needs to be a pointer to a pointer to your data.

RootTestData data; RootTestData* pdata = &data; TBranch* branch = tree->GetBranch("my_RootTestData"); branch->SetAddress(&pdata); branch->GetEntry(0); // fills data.

I guess this is so root can allocate memory and give you a pointer to it in the case you pass it a null pointer for the branch address. Still, it is confusing that it doesn’t mimic that behavior for built in data types. Trying to pass a pointer to a pointer to a double for example won’t work correctly, or at least it didn’t seem to.

So I guess my original question of why it wasn’t working is answered. However, I would like to know why/how root makes a distinction between built in data types and objects. In both cases root is just filling a memory address when you get the entry, right?

Thanks

For “creating branches” see http://root.cern.ch/root/html/TTree.html
For “reading branches” see http://root.cern.ch/root/html/TTree.html#TTree:GetEntry (don’t miss the “IMPORTANT NOTE” there in) and http://root.cern.ch/root/html/TBranchElement.html#TBranchElement:SetAddress