Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Tutorial] How to create ROOT Dictionary for a custom class is a stand-alone C++ project #13205

Merged
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
d942c13
Tutorial added, about how to create ROOT Dictionary for a custom class
atolosadelgado Jul 7, 2023
b33ef85
Update tutorials/tree/dictionary/README.md
atolosadelgado Jul 11, 2023
0bbff47
Update tutorials/tree/dictionary/CMakeLists.txt
atolosadelgado Jul 11, 2023
1104f8e
Update tutorials/tree/dictionary/README.md
atolosadelgado Jul 11, 2023
74f5837
Update tutorials/tree/dictionary/data2Tree.cpp
atolosadelgado Jul 11, 2023
c8cf91d
Update tutorials/tree/dictionary/data2Tree.cpp
atolosadelgado Jul 11, 2023
566019d
Update tutorials/tree/dictionary/readTree.cpp
atolosadelgado Jul 11, 2023
7bb519a
Update tutorials/tree/dictionary/writeTree.cpp
atolosadelgado Jul 11, 2023
5c87723
Update tutorials/tree/dictionary/writeTree.cpp
atolosadelgado Jul 11, 2023
ee0adaa
Functions writeTree and readTree are now compiled separataly as state…
atolosadelgado Jul 11, 2023
a476225
Unique pointer is used now for holding the TFile object as stated here
atolosadelgado Jul 11, 2023
c7be2ca
New line added to end of file as stated here
atolosadelgado Jul 11, 2023
3184750
delete half sentence, as stated here
atolosadelgado Jul 11, 2023
2bbd027
Removed PIC flag according to P. Canal,
atolosadelgado Jul 13, 2023
322663c
Removed include and link ROOT directories, removed ROOT flags, as sta…
atolosadelgado Jul 13, 2023
75fe159
Removed as stated in
atolosadelgado Jul 13, 2023
ea91a9b
comment added as stated in
atolosadelgado Jul 13, 2023
db5f376
header files updated as stated in
atolosadelgado Jul 13, 2023
c2811c0
std::unique_ptr<TTree> used, redundant write/close calls removed, as …
atolosadelgado Jul 13, 2023
ff8990a
close file call removed, as stated in
atolosadelgado Jul 14, 2023
c27ba72
Added property to export symbols and create data2TreeLib.lib file, as…
atolosadelgado Jul 14, 2023
f187820
space removed as stated in https://github.com/root-project/root/pull/…
atolosadelgado Jul 17, 2023
8918f82
opening of file method back to TFile::Open, check of file status impr…
atolosadelgado Jul 17, 2023
5d68c66
std:cerr replaced by std:cout for expected output, as stated in https…
atolosadelgado Jul 17, 2023
0566cd2
method to open file changed back to TFile::Open method, as stated in …
atolosadelgado Jul 17, 2023
9ee2527
ÂTTree::Fill method called just once after filling data into objects,…
atolosadelgado Jul 17, 2023
dabe214
debug message removed as stated in https://github.com/root-project/ro…
atolosadelgado Jul 17, 2023
556f639
Merge branch 'root-project:master' into custom_class_tree_example
atolosadelgado Jul 17, 2023
d998e6e
cerr replaced by cout
atolosadelgado Jul 18, 2023
caf5a05
cout replaced by cerr for 2 warning messages
atolosadelgado Jul 18, 2023
191250e
Merge branch 'root-project:master' into custom_class_tree_example
atolosadelgado Jul 19, 2023
5e694ff
Merge branch 'root-project:master' into custom_class_tree_example
atolosadelgado Aug 10, 2023
ddcef7e
Exception thrown if not possible to open file, as suggested in
atolosadelgado Aug 10, 2023
825ddc5
Extension hpp/cpp changed to hxx/cxx, according to
atolosadelgado Aug 10, 2023
7a2e0b0
Example of how to print values from a DataFrame, suggested by
atolosadelgado Aug 10, 2023
92ae246
Merge branch 'root-project:master' into custom_class_tree_example
atolosadelgado Aug 14, 2023
edb9278
Description of CMakelists updated, as suggested by
atolosadelgado Aug 14, 2023
264d23e
List of ROOT components simplified according to
atolosadelgado Aug 14, 2023
3ddec6f
Line removed according to
atolosadelgado Aug 14, 2023
03c72c6
Extended explanation about dictionary added, according to
atolosadelgado Aug 14, 2023
7df705d
clang format passes, as suggested in
atolosadelgado Aug 14, 2023
d835e4b
Note about the dot role at the end of the branch name, as suggested in
Aug 16, 2023
63f13e3
Little expansion of README file
Aug 16, 2023
f7f9fe4
Clang format is ok now...
Aug 16, 2023
238b980
Merge branch 'root-project:master' into custom_class_tree_example
atolosadelgado Aug 24, 2023
9ae5a98
Merge branch 'root-project:master' into custom_class_tree_example
atolosadelgado Aug 25, 2023
49ee1f3
More details in CMakeList added, according to
Aug 25, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions tutorials/tree/dictionary/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Copyright (C) 1995-2023, Rene Brun and Fons Rademakers.
# All rights reserved.
#
# For the licensing terms see $ROOTSYS/LICENSE.
# For the list of contributors see $ROOTSYS/README/CREDITS.

#####################################################################################################################

# Details about integrating ROOT into CMake projects:
# https://root.cern/manual/integrate_root_into_my_cmake_project/

#####################################################################################################################

# CMakeLists.txt for event package. It creates a library with dictionary and a main program
atolosadelgado marked this conversation as resolved.
Show resolved Hide resolved
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)

project(treeUsingCustomClass)

#---Locate the ROOT package and defines a number of variables (e.g. ROOT_INCLUDE_DIRS)
find_package(ROOT REQUIRED COMPONENTS MathCore RIO Hist Tree Net Graf3d Graf Gpad Thread TreePlayer)

#---Define useful ROOT functions and macros (e.g. ROOT_GENERATE_DICTIONARY)
include(${ROOT_USE_FILE})
atolosadelgado marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This way of including ROOT is a bit outdated, see a more modern way here: https://cliutils.gitlab.io/modern-cmake/chapters/packages/ROOT.html

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you! I think those tips are worth to be included as part of the official ROOT documentation :)


#---Add include directory of ROOT to the build
include_directories(${CMAKE_SOURCE_DIR})

# CMake function provided by ROOT, used to generate the dictionary file, G__data2Tree.cxx
# See this link for further details:
# https://root.cern/manual/io_custom_classes/#using-cmake
ROOT_GENERATE_DICTIONARY(G__data2Tree data2Tree.hpp LINKDEF data2TreeLinkDef.hpp)

#---Create a shared library from
# * the previously generated dictionary, G__data2Tree.cxx
# * the class implementation
add_library(data2TreeLib SHARED data2Tree.cpp G__data2Tree.cxx)
target_link_libraries(data2TreeLib ${ROOT_LIBRARIES} )

atolosadelgado marked this conversation as resolved.
Show resolved Hide resolved
#--- This is needed on Windows in order to export the symbols and create the data2TreeLib.lib file
if(MSVC)
set_target_properties(data2TreeLib PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
endif()

#---Create a main program using the library
add_executable(treeExample main.cpp readTree.cpp writeTree.cpp)
target_link_libraries(treeExample ${ROOT_LIBRARIES} data2TreeLib)

40 changes: 40 additions & 0 deletions tutorials/tree/dictionary/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
How to read and write custom classes in TTree
=============================================

# Starting steps

1. Build the project, it requires ROOT

```
cmake -B build -S .
cmake --build build
```

2. Run the executable

```
./build/treeExample
```

3. Inspect the output rootfile, and the source code.

jalopezg-git marked this conversation as resolved.
Show resolved Hide resolved
# Files

The TTree can be seen as a collection of objects (branches), with a number of attributes (leaves). The name of the branch corresponds to the name of the instantiated object. The name of the leaves corresponds to the name of the attributes.

The class that is present in the TTree is declared in the .hpp, and the methods defined in the .cpp.

The linkdef file contains some instructions for ROOT, to specify which classes will require a dictionary:
jalopezg-git marked this conversation as resolved.
Show resolved Hide resolved

```
#pragma link C++ class myFancyClass+;
```

If for example `std::vector` of such class is also used in the TTree as well, it should be notified to ROOT as another separated instruction:

```
#pragma link C++ class std::vector<myFancyClass>+;
```

# Links:
* ROOT documentation: https://root.cern/manual/io_custom_classes/
23 changes: 23 additions & 0 deletions tutorials/tree/dictionary/data2Tree.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Author: Alvaro Tolosa-Delgado CERN 07/2023
// Author: Jorge Agramunt Ros IFIC(Valencia,Spain) 07/2023

/*************************************************************************
* Copyright (C) 1995-2023, Rene Brun and Fons Rademakers. *
* All rights reserved. *
* *
* For the licensing terms see $ROOTSYS/LICENSE. *
* For the list of contributors see $ROOTSYS/README/CREDITS. *
*************************************************************************/

// This file contains the definition of the class method
// The class method was declared in the corresponding .hpp file

#include "data2Tree.hpp"

void myDetectorData::clear()
{
time = 0;
energy = 0;
detectorID = 0;
correlatedDetectors_v.clear();
}
33 changes: 33 additions & 0 deletions tutorials/tree/dictionary/data2Tree.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Author: Alvaro Tolosa-Delgado CERN 07/2023
// Author: Jorge Agramunt Ros IFIC(Valencia,Spain) 07/2023

/*************************************************************************
* Copyright (C) 1995-2023, Rene Brun and Fons Rademakers. *
* All rights reserved. *
* *
* For the licensing terms see $ROOTSYS/LICENSE. *
* For the list of contributors see $ROOTSYS/README/CREDITS. *
*************************************************************************/

// This file declares a class, with some members and method
// The definition of the method is done in the corresponding .cpp file

#ifndef __data2Tree__
#define __data2Tree__

#include <vector>

class myDetectorData {
public:
//-- Example of method...
void clear();

//-- Class members
//-- initialized by construction, C++11
double time = 1;
double energy = 2;
int detectorID = 3;
std::vector<double> correlatedDetectors_v = {1, 2, 3};
};

#endif
19 changes: 19 additions & 0 deletions tutorials/tree/dictionary/data2TreeLinkDef.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Author: Alvaro Tolosa-Delgado CERN 07/2023
// Author: Jorge Agramunt Ros IFIC(Valencia,Spain) 07/2023

/*************************************************************************
* Copyright (C) 1995-2023, Rene Brun and Fons Rademakers. *
* All rights reserved. *
* *
* For the licensing terms see $ROOTSYS/LICENSE. *
* For the list of contributors see $ROOTSYS/README/CREDITS. *
*************************************************************************/

// This file contains a selection of types that will be described in the ROOT dictionary.
// Further details in: https://root.cern/manual/io_custom_classes/#selecting-dictionary-entries-linkdefh

#ifdef __CLING__

#pragma link C++ class myDetectorData + ;

#endif
28 changes: 28 additions & 0 deletions tutorials/tree/dictionary/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Author: Alvaro Tolosa-Delgado CERN 07/2023
// Author: Jorge Agramunt Ros IFIC(Valencia,Spain) 07/2023

/*************************************************************************
* Copyright (C) 1995-2023, Rene Brun and Fons Rademakers. *
* All rights reserved. *
* *
* For the licensing terms see $ROOTSYS/LICENSE. *
* For the list of contributors see $ROOTSYS/README/CREDITS. *
*************************************************************************/

#include <iostream>

void writeTree();
void readTree();

int main()
{
std::cout << "Starting writeTree()..." << std::endl;
writeTree();
std::cout << "Starting writeTree()... Done! " << std::endl;
std::cout << "Starting readTree()..." << std::endl;
readTree();
std::cout << "Starting readTree()... Done! " << std::endl;

return 0;
}

48 changes: 48 additions & 0 deletions tutorials/tree/dictionary/readTree.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Author: Alvaro Tolosa-Delgado CERN 07/2023
atolosadelgado marked this conversation as resolved.
Show resolved Hide resolved
// Author: Jorge Agramunt Ros IFIC(Valencia,Spain) 07/2023

/*************************************************************************
* Copyright (C) 1995-2023, Rene Brun and Fons Rademakers. *
* All rights reserved. *
* *
* For the licensing terms see $ROOTSYS/LICENSE. *
* For the list of contributors see $ROOTSYS/README/CREDITS. *
*************************************************************************/

#include <iostream>

#include <TFile.h>
#include <TTree.h>
#include <TTreeReader.h>
#include <TTreeReaderValue.h>

#include "data2Tree.hpp"

void readTree()
{

std::unique_ptr<TFile> ifile { TFile::Open("testFile.root", "read") };
if ( !ifile || ifile->IsZombie() ) {
std::cerr << "Problem opening the file testFile.root" << std::endl;
return;
}

// Create a TTreeReader to read the tree named "myTree"
TTreeReader aReader("myTree", ifile.get() );

// Create TTreeReaderValues for the branches "branch1" and "branch2"
TTreeReaderValue<myDetectorData> branch1(aReader, "branch1.");
TTreeReaderValue<myDetectorData> branch2(aReader, "branch2.");

// Loop over the entries of the tree
while (aReader.Next()) {
if (branch1->time != 0)
std::cout << " -Branch1 : time: " << branch1->time << "\t energy: " << branch1->energy << std::endl;
else if (branch2->time != 0)
std::cout << " +Branch2 : time: " << branch2->time << "\t energy: " << branch2->energy << std::endl;
else
std::cerr << "WARNING: entry " << aReader.GetCurrentEntry() << " is empty! " << std::endl;
}

}

atolosadelgado marked this conversation as resolved.
Show resolved Hide resolved
59 changes: 59 additions & 0 deletions tutorials/tree/dictionary/writeTree.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Author: Alvaro Tolosa-Delgado CERN 07/2023
// Author: Jorge Agramunt Ros IFIC(Valencia,Spain) 07/2023

/*************************************************************************
* Copyright (C) 1995-2023, Rene Brun and Fons Rademakers. *
* All rights reserved. *
* *
* For the licensing terms see $ROOTSYS/LICENSE. *
* For the list of contributors see $ROOTSYS/README/CREDITS. *
*************************************************************************/

#include <iostream>

#include <TFile.h>
#include <TTree.h>
#include <TTreeReader.h>
#include <TTreeReaderValue.h>

#include "data2Tree.hpp"

void writeTree()
{
std::unique_ptr<TFile> ofile { TFile::Open("testFile.root", "recreate") };
atolosadelgado marked this conversation as resolved.
Show resolved Hide resolved
if ( nullptr == ofile ) {
std::cerr << " File not open." << std::endl;
atolosadelgado marked this conversation as resolved.
Show resolved Hide resolved
return;
}

std::unique_ptr<TTree> myTree = std::make_unique<TTree>("myTree", "");
myDetectorData obj_for_branch1;
myTree->Branch("branch1.", &obj_for_branch1);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just out of curiosity, is the dot at the end of the branch name needed?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The dot at the end of the branch is semantically relevant and recommended. If you have a dot at the end of the name and the branch contains an object that is split (which is the default), the sub-branch names will be prefixed by the name of the top level branch. WIthout the dot, the prefix is not there. For example with the dot you get the sub branch names:

branch1.time
branch1.energy

without it you get:

time 
energy

So in case there same data member name appears in multiple branches adding the dot removes ambiguities. (In this example, this is the case since we have 2 branches containing the same data type).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you very much! I think that information is important enough to be part of the documentation :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


myDetectorData obj_for_branch2;
myTree->Branch("branch2.", &obj_for_branch2);

for (int i = 0; i < 10; ++i) {
//-- if i is even, fill branch2 and set branch1's entry to zero
if (i % 2 == 0) {
obj_for_branch1.clear();
obj_for_branch2.time = i + 5;
obj_for_branch2.energy = 2 * i + 5;
obj_for_branch2.detectorID = 3 * i + 5;
}
//-- if i is odd, we do the opposite
else {
obj_for_branch2.clear();
obj_for_branch1.time = i + 1;
obj_for_branch1.energy = 2 * i + 1;
obj_for_branch1.detectorID = 3 * i + 1;
}
myTree->Fill();
}

myTree->Print();

ofile->Write(); // This write the files and the TTree
atolosadelgado marked this conversation as resolved.
Show resolved Hide resolved

}