Fill TBranch with different type of variables went wrong

Dear experts

I wanted to write a simple macro to practice filling TBranch object with different type of variables. But my code went wrong in root 6.08 on ubuntu_16.04_64bit. In addition, what puzzles me a lot is that my code works in root_5.34_36 on windows and root_6.08 on ubuntu_16.04_32bit ( built on a virtual-machine).

Many thanks in advance.

My code and result of running it is as follow:

struct info{
	Int_t i;
	Double_t j;
};
void test()
{	TFile *f1 = new TFile("test.root","RECREATE");
	info x;
	x.i=2;
	x.j=2.0;
	TTree *tree = new TTree("tree","tree test");
	tree->Branch("b",&x,"integer/I:doub/D");
	tree->Fill();
	tree->Write();
	tree->Scan();
	f1->Close();
}
   ------------------------------------------------------------
  | Welcome to ROOT 6.08/06                http://root.cern.ch |
  |                               (c) 1995-2016, The ROOT Team |
  | Built for linuxx8664gcc                                    |
  | From tag v6-08-06, 2 March 2017                            |
  | Try '.help', '.demo', '.license', '.credits', '.quit'/'.q' |
   ------------------------------------------------------------

root [0] 
Processing test.C...
************************************
*    Row   * b.b.integ *  b.b.doub *
************************************
*        0 *         2 * 1.61e-319 *
************************************
root [1] 

Your problem is padding between the variables: sizeof(info) != sizeof(Int_t) + sizeof(Double_t), i.e. 16 != 4 + 8. Another 4 bytes get inserted between your variables but ROOT requires the values to be contiguous in memory.

ROOT suggests to order the types by size helps, i.e. put the larger Double_t first, then the Int_t (and don’t forget to change the order in tree->Brnach as well) - search for “alignment” in https://root.cern.ch/doc/master/classTBranch.html

Another option is to force the compiler to omit padding by using a compiler specific pragma. You can insert #pragma pack(1) in front of your struct.

Thank you so much. I amended the order of two variables and the code succeeds. And the second solution works as well.

What’s more, I added "cout<< sizeof(info) << endl; " to my code and it showed me that on 32-bit-system, the size of “info” is 12 bytes, while it’s 16 bytes on 64-bit-system. And the original code works on windows system but fails on unbuntu. It seems plausible.

On win-64-bit

  *******************************************
  *                                         *
  *        W E L C O M E  to  R O O T       *
  *                                         *
  *   Version   5.34/36      5 April 2016   *
  *                                         *
  *  You are welcome to visit our Web site  *
  *          http://root.cern.ch            *
  *                                         *
  *******************************************

ROOT 5.34/36 (v5-34-36@v5-34-36, Apr 05 2016, 10:25:45 on win32)

CINT/ROOT C/C++ Interpreter version 5.18.00, July 2, 2010
Type ? for help. Commands must be C++ statements.
Enclose multiple statements between { }.
root [0]
Processing test.C...
************************************
*    Row   * n1.n1.in1 * n1.n1.do2 *
************************************
*        0 *         2 *         2 *
************************************
16

On ubuntu_32-bit

   ------------------------------------------------------------
  | Welcome to ROOT 6.08/06                http://root.cern.ch |
  |                               (c) 1995-2016, The ROOT Team |
  | Built for linux                                            |
  | From tag v6-08-06, 2 March 2017                            |
  | Try '.help', '.demo', '.license', '.credits', '.quit'/'.q' |
   ------------------------------------------------------------

root [0] 
Processing test.C...
************************************
*    Row   * n1.n1.in1 * n1.n1.do2 *
************************************
*        0 *         2 *         2 *
************************************
12

@pcanal @axel To me this seems to be a bug in ROOT 6.
Explicit “padding” management was required in ROOT 5 (i.e. one was often required to build dictionaries for such classes / structures).
However, as far as I understand it, this should no longer be needed in ROOT 6 (which I expect to automatically use its “just-in-time” compiler to deal with “padding”).

Thank you, Wile. I downloaded the root 5.34/36 for unbuntu 14_64_bit. The original code failed as well. Is there any difference between the compilers of the windows and linux?

The result is as follow:

ROOT 5.34/36 (v5-34-36@v5-34-36, Apr 05 2016, 10:25:45 on linuxx8664gcc)

CINT/ROOT C/C++ Interpreter version 5.18.00, July 2, 2010
Type ? for help. Commands must be C++ statements.
Enclose multiple statements between { }.
root [0] 
Processing test.C...
************************************
*    Row   * b.b.integ *  b.b.doub *
************************************
*        0 *         2 *         0 *
************************************
The size of "info" is 16 bytes.

Hi,

I bet TTree’s leaf offset “calculation” guesses (same in ROOT5 and 6) instead of using interpreter information of a mock-up struct. @pcanal will know!

In general it’s better to add two branches, one for &x.i and the second for &x.j. That way you’re independent of any offsets.

Axel.

Thanks a lot. It works.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.

One issue is one of backward compatibility. I.e. if somebody created (more or so by hand) a struct like things that match the current expectation of TTree. If we change those expectation to now match the one from the interpreter (and indeed then the compiler too), we will more or so silently break that code.

Better yet, when using the interpreter, you can abandon the leaflist alltogether:

struct info {
	Int_t i;
	Double_t j;
};
void test()
{	TFile *f1 = new TFile("test.root","RECREATE");
	info x;
	x.i=2;
	x.j=2.0;
	TTree *tree = new TTree("tree","tree test");
	tree->Branch("b",&x);
	tree->Fill();
	tree->Write();
	tree->Scan();
	f1->Close();
}

The same code will work when compiled if you generate the dictionary for ‘info’.

Cheers,
Philippe.

PS. Although the above code will create 2 branches rather than the single branch of the original case. This is likely to be an improvement but if not then you can use

tree->Branch("b",&x,32000,0);

to get back to a single branch.