ユーザ用ツール

サイト用ツール


サイドバー

Menu

Latest

study:software:root:cint

CINT

CINTの機能

インタープリターコマンド

インタープリターでのみ使えるコマンド。ここには一部紹介する。

もしマクロの中でこれらのコマンドを使いたいときはgROOT->ProcessLine(".x macro.cxx")などとすればいい。

動作 対応(?)メソッド
.q rootの終了。.qqqは強制終了。 gSystem->Exit(0)
.ls ファイルのオブジェクトの一覧表示 gDirectory->ls()
.x マクロの実行 gROOT->Macro()
.L マクロの読み込み(実行しない) gROOT->LoadMacro()
.undo 一つ前の宣言を取り消す
.pwd 現在のディレクトリ、pad、styleを表示 gDirectory->pwd()
.func 使える関数を一覧する
.! [command] shellのコマンドを実行 gSystem->Exec()
.bash bashの起動
.? 内部コマンド一覧

$ root macro.cxx

root [0] .x macro.cxx

root [0] .L macro.cxx
root [1] macro()

は全て同じ

.xや.Lはライブラリを読み込むこともできる。

全く重箱の隅をつつくような細かい話だけど、

[] .qhogehoge // .qとして解釈される

などとコマンドの後に余計な文字列があっても動作する。ROOTの方で意図しているかどうかは不明

出力をテキストにダンプする

標準出力をダンプしたい場合は、もちろんofstreamやfreopenを使ってもいいが、コマンドラインならば出力を簡単にテキストなどにリダイレクトすることができる。

root [] cout << "Hello!!" << endl; > Hello.txt //;は必須。
root [] cout << "Hello!!" << endl; >> Hello.txt //>>はファイルの最終行から追加出力。
root [] cerr << "Hello!!" << endl; 2> error.txt //2>は標準エラー出力をリダイレクト。
root [] cout << "Hello!!" << endl; >& std.txt //>&は標準出力と標準エラー出力。
root [] tree->Show("*"); > Show.txt //メソッドの出力をダンプすることもできる

  • ;を省略してはいけない。どこでコマンドが終わっているかわからないのでエラーになる
  • ファイルが存在しなくても自動的にテキストは生成される

CINTの独特の文法

I コマンドラインで可能
M macroで可能
M* macroで可能だがwarningがでる

cintはすべてのc++の機能をサポートするわけではないが、c++にはない独自の拡張を行なって利便性を上げている。

当然マクロをコンパイルするときはこの機能は全て使えない。互換性のあるマクロを書くためには、これらの機能を使わないように習慣づけた方がいい

宣言を省略して変数が使える

I M

c++でインスタンスを作りたい場合、下のようにする。

root [] TH1F *h1 = new TH1F(引数);

しかし、これはクラス名が二回も出てくるので、かなり書くのが面倒である。

cintでは下のように、インスタンスを代入すれば変数宣言を省略できる。

root [] h1 = new TH1F(引数);

また基本型の変数も宣言せずに値を代入した場合も

root [] a = 1;
Warning: Automatic variable a is allocated (tmpfile):1:

のようにして、自動的にデータ型を推論して生成する(この場合はint)。が、warningがでるのでうっとうしい。

ちなみに、初期化しないで変数を宣言した場合、自動的に0が入るようである。

root [] int a;
root [] cout << a << endl;
0

配列については宣言は省略できない。

root [] a[] = {0,1,2};
Syntax Error: a[]= (tmpfile):1:
*** Interpreter error recovered ***

ポインタのメンバ関数にアクセスするときに->ではなく.を使える

I M*

下のようにメンバーアクセスするときに.と->どちらでも使える。

root [] TH1F *hist = new TH1F("hist","title",100,0,100);
root [] hist->Fill(3);//本来はこうだけど
root [] hist.Fill(3); //ポインタに実体のようにアクセスできる

変数(実体)とポインタ変数をキャストによって変換できる

I M

何を言っているかわからないと思うが、俺にもわからねえ。

TH1F h("h","h",100,-5,5);
TH1F *ph = (TH1F*)h; //信じられないが、これは可能
//TH1F *ph = &h; //つまりこういうこと
TH1F *ph = h; //これはさすがに通らない

TH1F *ph2 = new TH1F("h","h",100,-5,5); 
TH1F h2 = (TH1F)ph2; //これも可能

ちなみに基本形(intとかdouble)では、こんなことはできないみたい。

これは推測だが、CINTではオブジェクトの実体とそれを指すポインタ変数の間に違いは設けられておらず、同じ存在として扱われるらしい。

付加的な要素として、実体として宣言されているか、ポインタとして宣言されているか、という情報も保持することで擬似的に2つを区別している、と考えられる。

ここまで来ると、CINTの実体はほとんど違う言語と言っても過言ではない。なんでこういう仕様にしたんだろうか。

オブジェクト名をポインタのように扱える

I M

これはcintがもつ特に重要な機能である。

rootのオブジェクトはポインタの名前とは別に、オブジェクト名というのをもっている。

例としてTH1Fの場合を書いておく。

root [] TH1F *ポインタ名 = new TH1F("オブジェクト名","ヒストグラムのタイトル",binの数,下限,上限);

ちなみに.lsコマンドで見えているのは(カレントディレクトリに存在する)オブジェクトのオブジェクト名とそのタイトルである

通常オブジェクトにアクセスするためにはポインタ変数を使うわけだが、rootではこのオブジェクト名を使ってオブジェクトにアクセスすることができる。

root [] gROOT->FindObject("オブジェクト名")->Draw();

gROOTはシステム情報などを管理するTROOT型のグローバル変数である(と言っても、TROOT型のオブジェクトはgROOTしか持てないが)。gROOTはROOTで生成されるほとんどのオブジェクトのListをメンバーに持っていて、そこからオブジェクトのアドレスを呼び出している。


ここまでが前置きだが、cintではこのアドレスを辿る作業を自動的にやってくれる。

すなわち、下のように、ポインタの名前を書く代わりにオブジェクト名を記述しても、メンバーアクセスができる。

root [0] TH1F *h1 = new TH1F("name","title",10,0.,5.);
root [1] name->Fill(3);
root [2] h2 = name; //こんな意味不明な文法もCINTではまかり通る

注意することとしてはオブジェクト名に演算子で使う文字を使うと、この機能は当然使えなくなる。

root [0] TH1F *h1 = new TH1F("name[0]","title",10,0.,5.);
root [1] name[0]->Fill(3);
Error: Symbol name[0] is not defined in current scope
Error: Failed to evaluate name[0]->Fill(3)
*** Interpreter error recovered ***
// 後は::とか.を名前に含めるとこれがおこる

ちなみに同名のポインタがあった場合はそちらが優先される

ダウンキャストを暗黙で行うことができる

I M

c++の文法ではアップキャスト(親クラスへのキャスト)は安全であることが保証されるので暗黙で行えるが、ダウンキャスト(派生クラスへのキャスト)は明示しないと行えない。

cintでは暗黙でこれが可能である。

root [] TH1F* h1 = new TH1F("h","title",100,-10,10);
root [] TH1F* h2 = gROOT->FindObject("h");
root [] //本来は下のようにキャストする必要がある
root [] //TH1F* h2 = (TH1F*)gROOT->FindObject("h");

;を省略できる。省略した場合、戻り値が出力される

I

root [] 3+4
(int)7

インタープリターコマンドが定義されている

I

詳細はインタープリターコマンドの項目で説明。

変数の寿命がローカルスコープの範囲外になってもつきない

I M

CINTのローカルスコープは関数スコープしか有効ではない。

例えばfor文を使ってもブロックの内側と外側でスコープの区別が存在しない。

void macro(){

  for(int i=0;i<5;++i){
  
    float b = 0.04;
    //本来はbはこの範囲の中でしか使えないが
  }
  
  std::cout << "i = " << i << std::endl;
  std::cout << "b = " << b << std::endl;
}

上を実行すると

i = 5
b = 0.04

となる。

このような仕様なのでコンパイルせずにCINTだけで大規模な解析をするのはとてもやりづらい。 複数人での開発となると実質不可能

同じ変数型なら何回変数宣言してもerrorを出さない

I M

 int a = 6;
 int a = 3;

みたいなことをしてもerrorを出さない。CINTにはローカルスコープが存在しないのに、あまり違和感なく使えるのはこの機能のおかげだと思う。

root [] int a = 6;
root [] float a = 3.0;
Error: Invalid type 'flaot' in declaration of 'a' test.cc:30:

違う型で宣言するとerrorになる。

配列の要素数にconstでない変数を使用できる

I M?

[] int n = 6;
[] float a[n] = {0};

のように配列の要素数に変数を使用できる。

本来c/c++の文法では

const int n = 6;
float a[n] = {0};

のようにconst修飾子をつけないと配列の要素数に変数は使えない。

constでない変数では値がコンパイルの時点で決まっていないということになり、つまり動的に配列を確保する必要があるので、これは許されていない。

※gccでは特別にconstをつけなくてもこれが可能になっている。

いまいちよくわからないのだが、CINTのマクロでは下のように出ることもある。

Error: Non-static-const variable in array dimension (macro name and line number):
 (cint allows this only in interactive command and special form macro which
  is special extension. It is not allowed in source code. Please ignore
  subsequent errors.)

CINTは挙動がよくよくわからないので、なるべく使わないほうが精神衛生上いいかもしれない。

配列の要素数を超えたindexにアクセスした場合にエラーが出る

I M

通常c言語では下のように

int x[] = {0,1,2};
x[4];

などと、配列の要素数より大きな数のindexにアクセスしたとしてもエラーを出してくれない。

CINTでは下のようにエラーを返す。

[] int x[] = {0,1,2};
[] x[4];
Error: Array index out of range x[4] -> [4]  valid upto x[2] (tmpfile):1:
*** Interpreter error recovered ***

がしかし、x[3]にアクセスしてもなぜかエラーを吐かなかった。なんだろこれ。

関数の戻り値の変数型を書かなくても、関数が使える

M

!?って感じなんだけど、なぜか関数の戻り値の変数型を書かなくても動いてしまう。

どういう意味かというと

hello(){
  printf("Hello\n");
}

macro(){
  hello();
}

上のような、完全にcの文法的におかしいマクロも使えてしまう。どうやら勝手にint型にしてreturn 0;するみたい。

[追記]私が知らなかっただけみたいで、昔のc言語(いつかはしらない)ではこうした暗黙の型宣言が許されていたらしい。

関数の再定義ができる

M

void func(){
    printf("hoge");
    }
void func(){
    printf("piyo");
} 

とした場合は後ろで定義した"piyo"が表示される。ちなみにinterpreterでは関数定義ができない。

include pathについて

I M

CINTでは

//c言語では本来、カレントディレクトリは探索しない
#include <hoge.h>

//c言語では本来、カレントディレクトリも含めて探索する
#include "hoge.h"

は同じ働きになる。どちらでもカレントディレクトリを含めて探索を行う。


ちなみにCINTでinclude pathを追加するオプションは多分ない。

ACLiCなら

ACLiC.IncludePaths:     -I/where/the/includes/are

gccなら

$ gcc -I/where/the/includes/are

でそれぞれinclude pathを追加できる。

CINTの場合なら代わりに

Unix.*.Root.MacroPath:      /where/the/includes/are

でmacro pathを追加し

[] .L hoge.h
[] //または
[] gROOT->LoadMacro("hoge.h");

とすればほぼ同じ働きになる。


ここからさらにマニアックな違いについて。c言語では

#include "/some/path/hoge.h"

のようにmain.cでincludeしたhoge.hの中で

#include "piyo.h"

とした場合、piyo.hが探索されるディレクトリは/some/path/、つまりhoge.hがあるディレクトリである。カレントディレクトリ、つまりmain.cのあるディレクトリは関係ない。これは直観的には当然に思える。

CINTでは驚異的なことにinclude pathでは/some/path/とカレントディレクトリの両方で探索される。このことはgROOT->LoadMacro()でも全く同じである。

つまりさっきの例で言うなら、piyo.hの探索順は

/some/where -> current dir -> unixの標準パス

となっている。

study/software/root/cint.txt · 最終更新: 2015/08/15 06:48 by kamo

ページ用ツール