Undefined symbols for architecture x86_64:
"TTreeReader::SetEntryBase(long long, bool)", referenced from:
TTreeReader::SetEntry(long long) in Analyze-0b59f2.o
"TTreeReader::SetTree(TTree*)", referenced from:
Analyze::Init(TTree*) in Analyze-0b59f2.o
"TTreeReader::~TTreeReader()", referenced from:
Analyze::~Analyze() in Analyze-0b59f2.o
"ROOT::Internal::TTreeReaderValueBase::GetAddress()", referenced from:
TTreeReaderValue<float>::Get() in Analyze-0b59f2.o
"ROOT::Internal::TTreeReaderValueBase::~TTreeReaderValueBase()", referenced from:
TTreeReaderValue<float>::~TTreeReaderValue() in Analyze-0b59f2.o
TTreeReaderValue<int>::~TTreeReaderValue() in Analyze-0b59f2.o
"Analyze::Class()", referenced from:
Analyze::IsA() const in Analyze-0b59f2.o
Analyze::ShowMembers(TMemberInspector&) const in Analyze-0b59f2.o
"Analyze::Streamer(TBuffer&)", referenced from:
vtable for Analyze in Analyze-0b59f2.o
"_main", referenced from:
implicit entry/start for main executable
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Analyze.cc:45:3: error: use of undeclared identifier 'Begin'
Begin();
^
Analyze.cc:46:3: error: use of undeclared identifier 'SlaveBegin'
SlaveBegin();
^
Analyze.cc:47:3: error: use of undeclared identifier 'Process'
Process();
^
Analyze.cc:48:3: error: use of undeclared identifier 'SlaveTerminate'
SlaveTerminate();
^
Analyze.cc:49:3: error: use of undeclared identifier 'Terminate'; did you mean
'std::terminate'?
Terminate();
^~~~~~~~~
std::terminate
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/exception:116:40: note:
'std::terminate' declared here
_LIBCPP_NORETURN _LIBCPP_FUNC_VIS void terminate() _NOEXCEPT;
^
5 errors generated.
The left problem may be because the methods in int main() are class methods which should be used after an object. Yet I don’t know what object of what class to declare.
What is does the header Analyze.h contain? I guess it defines the class Analyze, in that case you have to create an object of that type in your main function and call the methods on the object. Something like this (not tested!):
#ifndef Analyze_h
#define Analyze_h
#include "TROOT.h"
#include "TChain.h"
#include "TFile.h"
#include "TSelector.h"
#include "TTreeReader.h"
#include "TTreeReaderValue.h"
#include "TTreeReaderArray.h"
// Headers needed by this particular selector
class Analyze : public TSelector {
public :
TTreeReader fReader; //!the tree reader
TTree *fChain = 0; //!pointer to the analyzed TTree or TChain
// Readers to access the data (delete the ones you do not need).
TTreeReaderValue<Int_t> event = {fReader, "event"};
TTreeReaderValue<Float_t> ebeam = {fReader, "ebeam"};
TTreeReaderValue<Float_t> px = {fReader, "px"};
TTreeReaderValue<Float_t> py = {fReader, "py"};
TTreeReaderValue<Float_t> pz = {fReader, "pz"};
TTreeReaderValue<Float_t> zv = {fReader, "zv"};
TTreeReaderValue<Float_t> chi2 = {fReader, "chi2"};
Analyze(TTree * /*tree*/ =0) { }
virtual ~Analyze() { }
virtual Int_t Version() const { return 2; }
virtual void Begin(TTree *tree);
virtual void SlaveBegin(TTree *tree);
virtual void Init(TTree *tree);
virtual Bool_t Notify();
virtual Bool_t Process(Long64_t entry);
virtual Int_t GetEntry(Long64_t entry, Int_t getall = 0) { return fChain ? fChain->GetTree()->GetEntry(entry, getall) : 0; }
virtual void SetOption(const char *option) { fOption = option; }
virtual void SetObject(TObject *obj) { fObject = obj; }
virtual void SetInputList(TList *input) { fInput = input; }
virtual TList *GetOutputList() const { return fOutput; }
virtual void SlaveTerminate();
virtual void Terminate();
ClassDef(Analyze,0);
};
#endif
#ifdef Analyze_cxx
void Analyze::Init(TTree *tree)
{
// The Init() function is called when the selector needs to initialize
// a new tree or chain. Typically here the reader is initialized.
// It is normally not necessary to make changes to the generated
// code, but the routine can be extended by the user if needed.
// Init() will be called many times when running on PROOF
// (once per file to be processed).
fReader.SetTree(tree);
}
Bool_t Analyze::Notify()
{
// The Notify() function is called when a new file is opened. This
// can be either for a new TTree in a TChain or when when a new TTree
// is started when using PROOF. It is normally not necessary to make changes
// to the generated code, but the routine can be extended by the
// user if needed. The return value is currently not used.
return kTRUE;
}
#endif // #ifdef Analyze_cxx
As for your suggestion, I tried the exact code given by you, but the result was:
Analyze.cc:46:13: error: too few arguments to function call, expected 1, have 0
ana.Begin();
~~~~~~~~~ ^
Analyze.cc:10:1: note: 'Begin' declared here
void Analyze::Begin(TTree * /*tree*/)
^
Analyze.cc:47:18: error: too few arguments to function call, expected 1, have 0
ana.SlaveBegin();
~~~~~~~~~~~~~~ ^
Analyze.cc:21:1: note: 'SlaveBegin' declared here
void Analyze::SlaveBegin(TTree * /*tree*/)
^
Analyze.cc:48:15: error: too few arguments to function call, single argument
'entry' was not specified
ana.Process();
~~~~~~~~~~~ ^
Analyze.cc:26:1: note: 'Process' declared here
Bool_t Analyze::Process(Long64_t entry)
^
3 errors generated.
It seems that I need to put some arguments of class TTree in the parentheses, but there is nothing else than c1 and chi2Hist declared in the global scope. Should I declare something or read something from the file I need to analyze?
As a note, I put both the files in the same directory where the .root file I need to analyze is.
Yes, you need to read the tree you want to analyse from your root file, assuming your root file is named data.root and the tree you want to read is named nominal (you can always open the file in a TBrowser to find this out) the main should look something like:
int main() {
// Open the ROOT-file
TFile* f = TFile::Open("data.root", "READ");
// Fetch the TTree
TTree* t = nullptr;
if (f != nullptr && !f->IsZombie()) {
t = dynamic_cast<TTree*>(f->Get("nominal"));
}
// Abort if we can't get the TTree
if (t == nullptr) return 1;
// Set up the Analyze class
Analyze ana;
ana.Init(t); // Initialise the TTreeReader
ana.Begin(nullptr);
ana.SlaveBegin(nullptr);
// Loop through all entries
size_t nentries = t->GetEntries();
for (size_t ievent = 0; ievent < nentries; ++ievent) {
ana.Process(ievent);
}
ana.SlaveTerminate();
ana.Terminate();
f->Close();
}
Again, I haven’t tested this, but from the code you attached I think it should work.
Try adding -lTreePlayer by hand to the end of your compilation command. If I am not mistaken, -lTreePlayer is not added by default by the root-config script.
The construct dynamic_cast<TTree*> is used to check that the pointer being casted is really to a TTree.
If you have, say, a TFile* and try to use with dynamic_cast<TTree*>, it will give you a nullptr.
C-style casts are bad for multiple reasons:
0) in general casts are a sign that something type unsafe is going on and great care should be taken
C-style casts are hard to find in code. (xxx) could be anything from a cast to xxx or just parentheses in an arithmetic expression or function arguments.
C-casts can do multiple things from static_cast to reinterpret_cast.
C-cast are unsafe: consider a file with a TH1F named “h”. What happens if you do auto h=(TH1D*)file->Get("h");? Yes, undefined behavior! Just because you mixed up TH1F and TH1D. Even worse, the code might actually seem to work but break later at some unexpected place!
=> avoid C style casts in C++! C style cast are never “proper”. Never assume an object from a file is of the expected type. Always check!
Wile’s code is type safe, therefore better. It doesn’t contain a cast. (ok, it contains a cast, but it is hidden in the GetObject implementation - no problems there).
I prefer a function like Get but being type safe like GetObject. Easy to build your own.