If one does not use a dynamical allocation, it works. With a dynamicall alocation (useful if we wish to decide the size during the execution of the program), it gives a wrong value for a.
int nt()
{
int n1=10;
int n2=5;
//with next line, it works : the tree content is correct
//float a[10][5]; //works
//with this small block, it does not work : the tree content is not correct
float **a = new float * [n1];
for (int i=0;i<n1;i++)
a[i] = new float[n2];
//end small block
float b[n1][n2];
TFile *ff = new TFile("test.root","recreate");
TTree *tt = new TTree("tt","tt");
tt->Branch("a", &a, "a[10][5]/F");
tt->Branch("b", &b, "b[10][5]/F");
for (int i = 0; i < n1; i++) {
for (int j = 0; j < n2; j++) {
a[i][j] = 5;
b[i][j] = 3;
}
}
std::cout << a[0][0] << " " << b[0][0] << std::endl;
tt->Fill();
tt->Write();
ff->Close();
return 0;
}
ROOT Version: 6.18 Platform: linue Compiler: Not Provided
Ah I think I know! When you say the branch has leaflist "a[10][5]/F", TTree expects that that &a points to an area of memory with 50 consecutive floats (as it is when you declare the variable as float a[10][5]).
Instead when you dynamically allocate it each new float[5] is at a different memory location, and TTree ends up reading random bits at the moment of writing the data.
You can check by printing the difference between the addresses of the elements:
std::cout << unsigned(&a[n1-1][n2-1] - &a[0][0]) << std::endl; // prints 76 or 220 on my laptop (could print anything >= 49 really)
std::cout << unsigned(&b[n1-1][n2-1] - &b[0][0]) << std::endl; // will always print 49
@pcanal what’s the right way to dynamically allocate an array of arrays and write it with TTree?
EDIT:
btw stack-allocated arrays of dynamic size such as float b[n1][n2] are a non-standard language extension, you need constexpr int n1=10; constexpr int n2=5; to make it valid “ISO C++”
thank you. The initial code had const and I removed it just as a check.
Your idea of consecutive space it interesting. If so, then TTree would not allow this dyncamic allocation, which is somehow “standard”.
is not flexible and assume that the arguments describe the content (and does not look at the type of the pointer at all). And, as Enrico mentioned, it assume it is consecutive in memory.
In your example (maybe over-simplified for your real use case), the size are fixed. So you could also use
using f10 = float[10];
f10 *c = new float[5][10];
If you must use the float ** pattern, you will need to wrap it in a struct or class, generate the dictionary for that struct, compile and load it and create a branch based on that struct:
struct_with_array b;
tt->Branch("b", &b);
Note that if the dimension are actually variable, we can only support the outer dimension to be variable (an issue with missing syntax to tell the I/O what is the size of that dimensions).
To have truely variable number of variable dimensions you need to use nested vector.