インタープリターでのみ使えるコマンド。ここには一部紹介する。
もしマクロの中でこれらのコマンドを使いたいときは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()
は全て同じ
[] .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 //メソッドの出力をダンプすることもできる
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の数,下限,上限);
通常オブジェクトにアクセスするためにはポインタ変数を使うわけだが、rootではこのオブジェクト名を使ってオブジェクトにアクセスすることができる。
root [] gROOT->FindObject("オブジェクト名")->Draw();
ここまでが前置きだが、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
となる。
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になる。
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は挙動がよくよくわからないので、なるべく使わないほうが精神衛生上いいかもしれない。
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;するみたい。
M |
---|
void func(){ printf("hoge"); } void func(){ printf("piyo"); }
とした場合は後ろで定義した"piyo"が表示される。ちなみにinterpreterでは関数定義ができない。
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の標準パス
となっている。