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 all 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
49 changes: 49 additions & 0 deletions tutorials/tree/dictionary/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# 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 that creates a library with dictionary and a main program
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 Tree TreePlayer ROOTDataFrame)

#---Include a CMake module which makes use of the previous variables and loads modules
# with useful macros or functions such as ROOT_GENERATE_DICTIONARY
# For further details: https://root-forum.cern.ch/t/how-to-integrate-root-into-my-project-with-cmake/37175
include(${ROOT_USE_FILE})
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.hxx LINKDEF data2TreeLinkDef.hxx)

#---Create a shared library from
# * the previously generated dictionary, G__data2Tree.cxx
# * the class implementation
add_library(data2TreeLib SHARED data2Tree.cxx 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 writeTree.cxx readTree.cxx readTreeDF.cxx)
target_link_libraries(treeExample ${ROOT_LIBRARIES} data2TreeLib)

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

This example shows how to use ROOT as a toolkit to write and read a TTree containing custom classes.

# 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

This example shows how to use ROOT as a library in a C++ project. The C++ main function is defined in the file main.cpp. The code that writes the TTree onto disk is contained in the file writeTree.cxx. The files readTree.cxx and readTreeDF.cxx show two different ROOT interfaces to read a TTree. The files data2tree.* contain the code for the custom class that fills the TTree.

## Definition of custom class

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 file data2tree.hpp, and the methods defined in the file data2tree.cpp.

To be able to read and write objects of a particular user-defined type, ROOT I/O needs to know some information about the class/struct, e.g. the class members and their types, offset of each data member, etc. This information is contained in a ROOT dictionary; see [I/O of custom classes](https://root.cern/manual/io_custom_classes/#generating-dictionaries) for more information.

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.cxx
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.hxx"

void myDetectorData::clear()
{
time = 0;
energy = 0;
detectorID = 0;
correlatedDetectors_v.clear();
}
33 changes: 33 additions & 0 deletions tutorials/tree/dictionary/data2Tree.hxx
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.hxx
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
31 changes: 31 additions & 0 deletions tutorials/tree/dictionary/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// 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();
void readTreeDF();

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;
std::cout << "Starting readTreeDF()..." << std::endl;
readTreeDF();
std::cout << "Starting readTreeDF()... Done! " << std::endl;

return 0;
}
46 changes: 46 additions & 0 deletions tutorials/tree/dictionary/readTree.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// 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.hxx"

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;
}
}
25 changes: 25 additions & 0 deletions tutorials/tree/dictionary/readTreeDF.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// 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. *
*************************************************************************/

/* Example of reading a TTree using the RDataFrame interface
* Documentation of RDataFrame: https://root.cern/doc/master/classROOT_1_1RDataFrame.html
*/

#include <ROOT/RDataFrame.hxx>

#include "data2Tree.hxx"

void readTreeDF()
{

ROOT::RDataFrame df{"myTree", "testFile.root"};
df.Display({"branch1.time", "branch1.energy", "branch2.time", "branch2.energy"}, /*nRows*/ 10)->Print();
}
60 changes: 60 additions & 0 deletions tutorials/tree/dictionary/writeTree.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// 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.hxx"

void writeTree()
{
std::unique_ptr<TFile> ofile{TFile::Open("testFile.root", "recreate")};
if (!ofile || ofile->IsZombie()) {
throw std::runtime_error("Could not open file testFile.root");
}

std::unique_ptr<TTree> myTree = std::make_unique<TTree>("myTree", "");
myDetectorData obj_for_branch1;
myDetectorData obj_for_branch2;

// NOTE: the dot at the end of the branch name is semantically relevant and recommended
// because it causes the sub-branch names to be prefixed by the name of the top level branch.
// Without the dot, the prefix is not there.
// Here, objects of the same class appear in multiple branches, adding the dot removes ambiguities.
myTree->Branch("branch1.", &obj_for_branch1);
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;
atolosadelgado marked this conversation as resolved.
Show resolved Hide resolved
}
myTree->Fill();
}

myTree->Print();

ofile->Write(); // This write the files and the TTree
}
Loading