2.3 编写可编译执行的分析程序(1)¶

ROOT的运行环境( .x mycode.cpp) 下,代码将解释执行。这种方式运行速度慢,而且代码结构不利于编写复杂的分析程序。 本节介绍编译执行分析程序的方法,在程序中ROOT库与其他C++外部库一样通过include方式进行调用。

推荐用这种方法进行编写分析程序。

文件和目录的组织¶

主目录: ./tracking

      ./tracking 
          - main.cpp  - 主程序
          - Makefile -编译方法

      ./tracking/include/  
          - tracking.h (*.h, *.hh, *.hpp)  - 头文件

      ./tracking/src/ 
          - tracking.C (*.C, *.cc, *.cpp) -源文件


0. 利用 MakeClass 方法生成 .h 和 .C 文件¶

root -l f8ppac001.root

[0] tree->MakeClass("tracking")

生成 tracking.h 和 tracking.C

将tracking.h 移至 tracking/include目录, 将tracking.C 移至 tracking/src目录¶

1. main.cpp -- main 函数中写程序的主要流程。¶

  • 在./tracking目录编辑main.cpp
#include <iostream> 
#include <TFile.h>
#include <TTree.h>
#include <TString.h>
#include "tracking.h"
using namespace std;

int main(int argc, char* argv[])
{
   if(argc != 2) {
       cout<<"Usage: ./"<<argv[0]<<" run_number "<<endl;
       return -1;
   }

   int run_number = atoi(argv[1]);
   TString InputPath, OutputPath, infile, outfile;  

   InputPath = "./";
   OutputPath = "./";
   infile.Form("%sf8ppac%03d.root", InputPath.Data(), run_number);
   outfile.Form("%sout%03d.root", OutputPath.Data(), run_number);

    //input
   TFile *ipf = new TFile(infile);
   if(!ipf->IsOpen()) {
       cout<<"Cannot open input file: "<<infile<<endl;
       return -1;
   }
   TTree *ipt = (TTree*)ipf->Get("tree");

   //output
   TFile *opf = new TFile(outfile,"RECREATE");
   TTree *opt = new TTree("tree","ppac tracking");

   cout << "Inputfile: " <<infile.Data() << endl;
   cout << "Outputfile: " <<outfile.Data() << endl;

   tracking *tk = new tracking(ipt); //当traking构造函数中传入的ipt已存在时,构造函数内部的tree指针就指向ipt
   tk->Loop(opt);

    //

   ipf->Close();
   opf->Close();
   return 1;        

}

2.头文件(.h):写类的声明(包括成员和方法的声明)、函数原型、#define常数。¶

在写头文件时需要注意,在开头和结尾处必须按照如下样式加上预编译语句(如下):

#ifndef TRCKING_H
   #define TRCKING_H

   //你的代码写在这里

   #endif

这样做是为了防止重复编译,不这样做就有可能出错。 至于"tracking_h"这个名称,只要不与已存在的定义重合可以随意取。一般建议写成这种形式(或大写成 TRACKING_H),因为这样比较容易和头文件的名字对应。

修改tracking.h¶

  • 一般的编程规范推荐在头文件中只进行变量和函数原型声明,而不写出函数的具体实现。按照上述原则应将原 tracking.h 目录下的成员函数的具体实现部分挪至 src/tracking.cpp 文件内。为了尽量少修改MakeClass自动生成的代码,以下代码只添加用户代码,不做其他改动。

//////////////////////////////////////////////////////////
// This class has been automatically generated on
// Wed Mar 11 09:58:39 2020 by ROOT version 6.18/04
// from TTree tree/tree
// found on file: f8ppac001.root
//////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////
// This class has been automatically generated on
// Wed Mar 11 09:58:39 2020 by ROOT version 6.18/04
// from TTree tree/tree
// found on file: f8ppac001.root
//////////////////////////////////////////////////////////

#ifndef tracking_h
#define tracking_h

#include <TROOT.h>
#include <TChain.h>
#include <TFile.h>
#include <TH2.h>

// Header file for the classes stored in the TTree if any.

class tracking {
public :
   TTree          *fChain;   //!pointer to the analyzed TTree or TChain
   Int_t           fCurrent; //!current Tree number in a TChain

// Fixed size dimensions of array or collections stored in the TTree if any.

   // Declaration of leaf types
   Float_t         PPACF8[5][5];
   Float_t         F8PPACRawData[5][5];
   Int_t           beamTrig;
   Int_t           must2Trig;
   Float_t         targetX,targetY;

     //----- user-defined variables -------
   Double_t xx[3],xz[3],yy[3],yz[3];//1A,2A,3
   Double_t xx2b[2],yy2b[2],xz2b,yz2b;//2B x,y, 0-measured, 1- fitted.
   Double_t dx[3],dy[3];//residual
   Double_t tx,ty;//target position
   Double_t c2nx,c2ny;//chi2/ndf for xfit,yfit

   // List of branches
   TBranch        *b_PPACF8;   //!
   TBranch        *b_F8PPACRawData;   //!
   TBranch        *b_beamTrig;   //!
   TBranch        *b_must2Trig;   //!
   TBranch        *b_targetX;   //!   
   TBranch        *b_targetY;   //!

   tracking(TTree *tree=0);
   virtual ~tracking();
   virtual Int_t    Cut(Long64_t entry);
   virtual Int_t    GetEntry(Long64_t entry);
   virtual Long64_t LoadTree(Long64_t entry);
   virtual void     Init(TTree *tree);
   virtual void     Loop(TTree *tree);
   virtual Bool_t   Notify();
   virtual void     Show(Long64_t entry = -1);

   //----- user-defined methods -----
   virtual void     SetOutBranch(TTree *tree);
   virtual void     TrackInit();
   virtual void     SetTrace(TH2D *h,Double_t k,Double_t b,Int_t min,Int_t max);    
};

#endif

#ifdef tracking_cxx
tracking::tracking(TTree *tree) : fChain(0) 
{
// if parameter tree is not specified (or zero), connect the file
// used to generate this class and read the Tree.
   if (tree == 0) {
      TFile *f = (TFile*)gROOT->GetListOfFiles()->FindObject("f8ppac001.root");
      if (!f || !f->IsOpen()) {
         f = new TFile("f8ppac001.root");
      }
      f->GetObject("tree",tree);

   }
   Init(tree);
}

...

void tracking::Init(TTree *tree)
{
   // The Init() function is called when the selector needs to initialize
   // a new tree or chain. Typically here the branch addresses and branch
   // pointers of the tree will be set.
   // 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).

   // Set branch addresses and branch pointers
   if (!tree) return;
   fChain = tree;
   fCurrent = -1;
   fChain->SetMakeClass(1);

   fChain->SetBranchAddress("PPACF8", PPACF8, &b_PPACF8);
   fChain->SetBranchAddress("F8PPACRawData",  F8PPACRawData, &b_F8PPACRawData);
   fChain->SetBranchAddress("beamTrig", &beamTrig, &b_beamTrig);
   fChain->SetBranchAddress("must2Trig", &must2Trig, &b_must2Trig);
   fChain->SetBranchAddress("targetX",&targetX,&b_targetX);
   fChain->SetBranchAddress("targetY",&targetY,&b_targetY);
   Notify();
}

...

#endif // #ifdef tracking_cxx

2. 修改tracking.C文件 ¶

  • 在原MakeClass生成的tracking.C的代码的基础上进行修改,实现用户定义的函数。
  • 注意:必须包含 #include "tracking.h"。
#define tracking_cxx
#include <TH2.h>
#include <TStyle.h>
#include <TCanvas.h>
#include <TF1.h>
#include <TFitResult.h>
#include <TGraph.h>
#include "tracking.h"
using namespace std;

void tracking::SetBranch(TTree *tree)
{
  //measured pos
  tree->Branch("xx", &xx, "xx[3]/D");//1A,2A,3
  tree->Branch("xz", &xz, "xz[3]/D");
  tree->Branch("yy", &yy, "yy[3]/D");
  tree->Branch("yz", &yz, "yz[3]/D");

  ...
}

void tracking::TrackInit()
{
  tx = -999;
  ty = -999;

  //1A
  xx[0] = PPACF8[0][0];
  yy[0] = PPACF8[0][1];
  xz[0] = PPACF8[0][2];
  yz[0] = PPACF8[0][3];

  ...

}

void tracking::SetTrace(TH2D *h, Double_t k, Double_t b, Int_t min, Int_t max){
    if(h == 0) return;
    if(min >= max) return;

    ...
}


void tracking::Loop(TTree *tree)
{
   TH2D *htf8x = new TH2D("htf8x", "x trace by ppac", 2200, -2000, 200, 300, -150, 150);
   TH2D *htf8y = new TH2D("htf8y", "y trace by ppac", 2200, -2000, 200, 300, -150, 150);
   TH2D *htar = new TH2D("htar", "distribution on target", 100, -50, 50, 100, -50, 50);

   SetBranch(tree);

   if (fChain == 0) return;
   Long64_t nentries = fChain->GetEntriesFast();
   Long64_t nbytes = 0, nb = 0;
   for (Long64_t jentry=0; jentry<nentries;jentry++) {
      Long64_t ientry = LoadTree(jentry);
      if (ientry < 0) break;
      nb = fChain->GetEntry(jentry);   nbytes += nb;

      TrackInit();
      ...
       }
    }
   ...
}

3. 修改Makefile¶

  • makefile:告诉make命令如何编译和链接程序

参考链接:跟我一起写Makefile:https://blog.csdn.net/xiaoshuai537/article/details/79340153

下面的Makefile中ROOTCFLAGS,ROOTLIBS,ROOTGLIBS是编译和链接ROOT环境的必要语句。

OBJ = 程序编译后的名称

下载Makefile

#############################################################################
OBJ = tracking
MainFile = main.cpp

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

SourceFile := $(wildcard $(shell pwd)/src/*.c $(shell pwd)/src/*.cc $(shell pwd)/src/*.C $(shell pwd)/src/*.cpp $(shell pwd)/src/*.cxx)
IncludeFile := $(wildcard $(shell pwd)/include/*.h $(shell pwd)/include/*.hh $(shell pwd)/include/*.hpp)

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

ROOTCFLAGS  = $(shell root-config --cflags)
ROOTLIBS    = $(shell root-config --libs)
ROOTGLIBS = $(shell root-config --glibs)

GXX = g++ 
# -Wl ,--no-as-needed
DIR_INC = -I$(shell pwd)/include
CFLAGS = -Wall -O2 $(ROOTCFLAGS) $(DIR_INC)  #动态连接库不要写在这里
LIBS = $(ROOTGLIBS) -lSpectrum   #动态连接库写在这里 

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

all:$(OBJ)
$(OBJ): $(MainFile) $(SourceFile)
    $(GXX) $(CFLAGS) -o $@ $(MainFile) $(SourceFile) $(LIBS)
    @echo "=============================================================="
    @echo "$@ done !"
    @echo "=============================================================="
clean:
    rm -f *.o *.d $(OBJ)

使用方法:¶

make clean

make

  • ROOT环境内直接用.x myClass.C运行时 ROOT 自动载入所用函数的库文件,因此不用显式包含库文件;make编译时要求用户在代码中显式包含库文件。

    • 如程序内使用了TGraph,则需要在头部加入
#include <TGraph.h>
  • c++或ROOT的库文件:include 头文件用尖括号<>:
#include <iostream>
      #include <TGraph.h>
  • 用户编写的的头文件用双引号”“:如
#include "mylib.h"
  • 未包含函数的库文件是root编程中最常见的错误。

  • 直接使用cout, 或cin等在 std 的 namespace 上定义的函数也会报错。

    • 加入using namespace std; 或直接使用std::cout,std::cin(推荐)
  • C++编译器编译报错给出一堆错误信息时,一般只修正出现的第一个错误信息,然后再次编译。按上述步骤依次进行修改。

  • 编译成功时输出如下的信息:


g++  -Wall -O2 -I/Users/zhli/ROOT/root61804/include  -I/Users/zhli/ana/course/data_analysis/chapt2/compile/include -I/Users/zhli/ROOT/root61804/include  -L/Users/zhli/ROOT/root61804/lib -lCore -lImt -lRIO -lNet -lHist -lGraf -lGraf3d -lGpad -lROOTVecOps -lTree -lTreePlayer -lRint -lPostscript -lMatrix -lPhysics -lMathCore -lThread -lMultiProc -lROOTDataFrame -lpthread -stdlib=libc++ -lm -ldl -lSpectrum -lXMLParser  -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -pthread -stdlib=libc++ -std=c++11 -m64 -I/Users/zhli/ROOT/root61804/include -L/Users/zhli/ROOT/root61804/lib -lCore -lImt -lRIO -lNet -lHist -lGraf -lGraf3d -lGpad -lROOTVecOps -lTree -lTreePlayer -lRint -lPostscript -lMatrix -lPhysics -lMathCore -lThread -lMultiProc -lROOTDataFrame -lpthread -stdlib=libc++ -lm -ldl -L/Users/zhli/ROOT/root61804/lib -lGui -lCore -lImt -lRIO -lNet -lHist -lGraf -lGraf3d -lGpad -lROOTVecOps -lTree -lTreePlayer -lRint -lPostscript -lMatrix -lPhysics -lMathCore -lThread -lMultiProc -lROOTDataFrame -lpthread -stdlib=libc++ -lm -ldl -o tracking main.cpp /Users/zhli/ana/course/data_analysis/chapt2/compile/src/tracking.C
==============================================================
tracking done !
==============================================================

./tracking 1

In [1]:
!ls -R
2.3_comiling_1.html	f8ppac001.root		out001.root
2.3_comiling_1.ipynb	include			src
Makefile		main.cpp		tracking
Untitled.ipynb		main.cpp~

./include:
tracking.h	tracking.h~

./src:
tracking.C	tracking.C~

In [ ]:
!./tracking 1
Inputfile: ./f8ppac001.root


Outputfile: ./out001.root