TClass and std::vector

I am a bit puzzled about dictionaries for STL containers. There are various things that I don’t understand:

  1. I use TClass::GetClass to determine if a class have a valid dictionary, but it seems that for std::vector that method always return a non-null value:
root [0] TClass::GetClass("NonExistingClass")
(TClass *) nullptr
root [1] TClass::GetClass("std::vector<NonExistingClass>")
(TClass *) 0x557daf866950 

I don’t understand why the vector has a valid TClass, and whether this means that it also have a valid dictionary or not (specifically, valid for I/O on Root files).

  1. It seems that even without a manually created dictionary an std::vector can be written on a branch. Compiling this code:
#include "TTree.h"
#include "TFile.h"
#include <iostream>

class TestClass{
	public:
		int i;
};

int main(){
	// Write
	std::vector<TestClass> v;
	void *pv = &v;
	TFile *outFile = TFile::Open("test.root", "RECREATE");
	TTree *outTree = new TTree("outTree", "");
	outTree->Branch("vectBranch", "vector<TestClass>", pv);
	for(int i = 0; i < 5; ++i){
		v.resize(0);
		for (int iv = 0; iv < i; ++iv){
			v.push_back(TestClass{iv});
		}
		outTree->Fill();
	}
	outFile->Write();
	outFile->Close();
	delete outFile;
}

with gcc and running it I get no error and an output file containing the data:

$ root test.root 
root [0] 
Attaching file test.root as _file0...
(TFile *) 0x556946b1b3b0
root [1] .ls
TFile**         test.root
 TFile*         test.root
  KEY: TTree    outTree;1
root [2] outTree->Print()
******************************************************************************
*Tree    :outTree   :                                                        *
*Entries :        5 : Total =            1065 bytes  File  Size =        532 *
*        :          : Tree compression factor =   1.19                       *
******************************************************************************
*Br    0 :vectBranch : vector<TestClass>                                     *
*Entries :        5 : Total  Size=        667 bytes  File Size  =        133 *
*Baskets :        1 : Basket Size=      32000 bytes  Compression=   1.19     *
*............................................................................*

So it seems that something is written on file, although it might be (or actually is?) garbage. If data is actually garbage, shouldn’t it be safer to raise an error instead of silently going on? If data is good, how can Root write on disk data of a class (TestClass) which supposedly doesn’t have a dictionary (see 1) )?

  1. To read out data a dictionary seems to be necessary:
#include "TTree.h"
#include "TFile.h"
#include <iostream>

class TestClass{
	public:
		int i;
};

int main(){
	// Read
	std::vector<TestClass> *inv;
	TFile *inFile = TFile::Open("test.root");
	TTree *inTree = (TTree*)(inFile->Get("outTree"));
	inTree->SetBranchAddress("vectBranch", &inv);
	for(int iEv = 0; iEv < inTree->GetEntries(); ++iEv){
		inTree->GetEntry(iEv);
		std::cout << "Event " << iEv << ": ";
		for(int iv = 0; iv < inv->size(); ++iv){
			std::cout << (*inv)[iv].i << " ";
		}
		std::cout << std::endl;
	}
	inFile->Close();
}

Running this program gives a segfault:

$ ./a.out 
Error in <TTree::SetBranchAddress>: The class requested (vector<TestClass>) for the branch "vectBranch" is an instance of an stl collection and does not have a compiled CollectionProxy. Please generate the dictionary for this collection (vector<TestClass>) to avoid to write corrupted data.

 *** Break *** segmentation violation
Event 0: 

This seems reasonable, although I’d say that a dictionary would be needed also for writing so this makes the behavior described in 2) even more puzzling.

I couldn’t find any documentation explaining the details I’m interested in, so I’d be grateful if an expert can shed sone light on these. Thanks.

I don’t understand why the vector has a valid TClass, and whether this means that it also have a valid dictionary or not (specifically, valid for I/O on Root files).

TClass is able to be created if there is enough information known about the class by the interpreter (aka cling).

To check if you have a dictionary for a given TClass, look at the result of IsLoaded:

TClass::GetClass("std::vector<NonExistingClass>")->IsLoaded();

In addition for std::vector, we know the memory layout and can (sometimes) store it without the dictionary for the vector itself (but you need the dictionary for the content).

Running this program gives a segfault:

Most likely this is because of the uninitialized pointer, use:

std::vector<TestClass> *inv = nullptr;

Cheers,
Philippe.

Hi Philippe,
I don’t understand how cling (specifically, TClass) can know something about TestClass. The code I posted is compiled with GCC and not digested by the interpreter, so there’s actually no way for the Root internals to know about TestClass, as far as I understand, and indeed there’s no TClass for TestClass. Still, there’s a TClass for std::vector<TestClass>… but I think that it cannot be sufficient for reading data without properly generated dictionary. Indeed, the read program (compiled with GCC as well) crashes, and not due to the uninitialized pointer but due to the fact that the SetBranchAddress fails because of the missing dictionary.
In the end: wouldn’t it be better to have a TClass only for those specialization of std::vector which have a proper dictionary?

Humm … right … this negative side effect is a consequence of the way the support for vector of enum is supported. In several circumstances there is no dictionary information for enum and hence the mechanism that support STL containers without explicit dictionary (which makes life easier in many cases) has to assume that if there is no dictionary information then it must be an enum type.

Cheers,
Philippe.

I understand. So I stumbled upon a corner case, I suppose I have to live with it. By the way, what can I expect by calling TClass::GetClass(“std::vector”)->IsLoaded()? I’m asking because I see strange things (always in code compiled with GCC): if SomeClass Is TH1F and I don’t generate a dictionary for the vector specialization I get false, while I’m getting true if SomeClass is one of my custom classes (the custom class has a dictionary but the vector specialization has not). I’m trying to put together a reproducer but I’m finding difficulties with that.

It should only return true if there is a dictionary for it … However, for STL containers, dictionaries are generated automatically for you if they are used in a class for which you explicitly requested a dictionary.

Yes, my bad. The dictionary for the vector specialization was actually defined in another library which I linked to my executable, but I lost track of it. Thanks for your support!

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