Filling TTree with strings insert only first TString

ROOT Version:6.24/06
Platform: linux/docker

Hello,
I’m trying to create a TTree with a std::vector wich has inside a TString.
The problem is that only first string is stored and all other are ignored.

I will provide a test code to reprocude this.

Test.h

#pragma once
#include <TString.h>
#include <vector>

namespace TS {

	class TestClass {
	public:
		TestClass() {
			aString = "0";
			id = 0;
		}
		TString aString;
		int id;
	};

	class TestContainer {
	public:
		std::vector<TestClass> classes;
	};
}

Test.cpp

#include "Test.h"
void Test(){

  cout << "hello world" << endl;

  TS::TestContainer container;
  for (int i = 1; i <= 10; ++i) {
      TS::TestClass t;
      t.aString.Form("A new string %d", i);
      t.id = i;
      container.classes.push_back(t);
  }
   
  TFile *f = new TFile("file.root","RECREATE");
  TTree test("TreeTest", "");

  test.Branch("TClasses", &container.classes);
  test.Fill();
  test.Write();
  f->Close();
}

This is what I get in ROOT:

As you can see, TClasses.id is well populated, but the TString is stored only once.
What I’m wrong?

TTree::Scan does not handle collection of strings well. To actually test whether this works or not you would need to read the data explicit (SetBranchAddress, GetEntry, etc.)

I found a way to print with Scan. I need to use TString.Data() method:
fix

Now I’ve another problem. I cannot access single string value from script. I used TreeTest->MakeClass("treetest") to generate .C and .h to easly read data from script.
If I add this code in Loop() function:

...
for (int i = 0; i < kMaxTClasses; ++i) {
      std::cout << i << " | id: " << TClasses_id[i] << " | aString: " << TClasses_aString[i] << std::endl;
}
...

I get this output:
image

Seems like it can’t read TString properly from that tree. I used std::string as well, but the ouput is the same.

MakeClass is explicitly not allowing to access the objects but instead there decomposition and the generator may or may not be wrong.

For your use case, I strongly recommend that you investigate using RDataFrame instead of MakeClass (MakeClass has many limitation that are solved by RDataFrame).

I see. But I really don’t know how access data from an RDataFrame. Can you help me accessing that strings from it?
What I’ve done is loading RDataFrame with this code:

ROOT::RDataFrame d("TreeTest", "file.root");

I see the column names if use .GetColumnNames but if I use .GetColumnType("TClasses.aString") I get "ROOT::VecOps::RVec<TString>".
Now how can access a selected range of that strings?

Hi @TheEnigmist ,
RDF is all about accessing data. You have different ways to do so depending on what you want to do with it. If you want to extract the vector of strings from each event, you can use auto strings = d.Take<ROOT::RVec<TString>>("TClasses.aString");. If you want to perform some operation on each vector of strings, you want Foreach:

df.Foreach([] (const ROOT::RVec<TString> &strings_per_event) { ... },
           {"TClasses.aString"});

etcetera. See the user guide here and the tutorials here.

Cheers,
Enrico