Home | News | Documentation | Download

ROOT stand alone program compilation problem


#1

Hi,

I was trying to build a stand-alone program suggested in a ROOT tutorial.

The code was originally generated by MakeSelector("Analyze") , and I added the #ifdef ... part referred from ROOT Wiki book.

Here is my code:

#define Analyze_cxx
#include "Analyze.h"
#include "TTree.h"
#include "TH1.h"
#include "TCanvas.h"

TCanvas* c1 = NULL;
TH1* chi2Hist = NULL;

void Analyze::Begin(TTree * /*tree*/)
{
   TString option = GetOption();

   c1 = new TCanvas("c1","c1",900,700); // Set an empty canvas

   chi2Hist = new TH1F("chi2","Histogram of Chi2",100,0,20);
   chi2Hist->GetXaxis()->SetTitle("chi2");
   chi2Hist->GetYaxis()->SetTitle("number of events");
}

void Analyze::SlaveBegin(TTree * /*tree*/)
{
   TString option = GetOption();
}

Bool_t Analyze::Process(Long64_t entry)
{
   fReader.SetEntry(entry);

   /* Loop Section */
   //tree1->GetEntry(entry);
   chi2Hist->Fill(*chi2);
   return kTRUE;
}

void Analyze::SlaveTerminate() {}

void Analyze::Terminate()
{
   chi2Hist->Draw();
}

# ifdef __CINT__
int main(){
  Begin();
  SlaveBegin();
  Process();
  SlaveTerminate();
  Terminate();
  return 0;
}
# endif

I tried to compile it with

g++ -o Analyze Analyze.cc `root-config --cflags --glibs`

but there was this error message:

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)

Does anyone know the solution?


#2

You have a typo in your code:

# ifdef __CINT__

should be

# ifndef __CINT__

The way you wrote it hides the main function from the compiler, while you want to hide if from the interpreter.


#3

It is very strange that g++ produces a clang error message.


#4

The gcc package shipped by default in OSX is just a front-end to LLVM, so that would explain that.


#5

Thanks. That reduces the error message to

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.


#6

By the way, though it’s not related to the problem, there is always an error message by Syntastic:

./Analyze.h|11 col 10 error| fatal error: 'TROOT.h' file not found

Is there a way to fix it?


#7

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!):

int main(){
  Analyze ana;
  ana.Begin();
  ana.SlaveBegin();
  ana.Process();
  ana.SlaveTerminate();
  ana.Terminate();
  return 0;
}

As you your problem with Syntastic, maybe this is not the right forum for it but you have to point it to where the ROOT-headers are. You can find more information in the Syntastic documentation: https://github.com/vim-syntastic/syntastic/wiki/Configuration-Files


#8

The header Analyze.h contains:

#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.


#9

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.


#10

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.


#11

Thanks! The code can’t be compiled without this flag.


#12

Thanks! The code works well now, but I wonder why you use the type dynamic_cast<TTree*> instead of the regular (TTree*).

For completeness of this question, the original code gave nothing after execution, so I added

c1->Print("chi2.pdf",".pdf");

in Terminate() to have my graph printed.


#13

Hi,

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.


#14

The proper way to achieve it is:
TTree *t; f->GetObject(“nominal”, t);


#15

How is this way “proper”? It seems the original way is more straightforward visually. Is the GetObject() more suitable for fetching a tree?


#16

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

  1. 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.
  2. C-casts can do multiple things from static_cast to reinterpret_cast.
  3. 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.


#17

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