ROOTを使ってPythonとC++をバインドする

このサイトのチュートリアルが非常に良さげなので、このサイトを参考にしてROOTを使ったC++プログラミングを学習してみる。

スポンサーリンク

PythonとC++統合にROOTを使う

PyROOT is not just a set of Python bindings to C++ functions; it dynamically generates bindings for all C++ classes and functions in its namespace.
PyROOTはC++関数に対するただの一連のPythonバインディングではなく、自身のネームスペース内に、全てのC++クラスと関数用のバインディングを生成する。

Including new functions that you define.
もちろん、ユーザー定義関数も含まれている。

import ROOT
Welcome to JupyROOT 6.16/00

In a script, use gInterpreter.ProcessLine or gInterpreter.Declare to run C++ code.
スクリプト内では、gInterpreter.ProcessLine、もしくは、gInterpreter.Declareを用いてC++コードを実行する。

ROOT.gInterpreter.Declare("""
double dostuff(double a, double b) {
    return a + b;
}
""")
True
ROOT.dostuff(3.14, 100)
103.14

Suppose we want to change the dostuff function, common on a prompt or in a notebook.
dostuff関数を変えたいと仮定しよう。

ROOT.gInterpreter.Declare("""
double dostuff(double a, double b) {
    return (a + b) / sqrt(a*a + b*b);
}
""")
False
input_line_38:2:8: error: redefinition of 'dostuff'
double dostuff(double a, double b) {
       ^
input_line_36:2:8: note: previous definition is here
double dostuff(double a, double b) {
       ^

Ouch! Of course it doesn’t let us replace a definition (not legal in C++), but we really want to.
もちろん、定義を差し替えることは(C++では不正なため)許されないのだが、どうしても定義を変えたい。

This is what I do:
そういう場合は以下のようにするとよい。

tmpname = "dostuffv1"                  # keep changing the C++ name

ROOT.gInterpreter.Declare("""
double %s(double a, double b) {
    return (a + b) / sqrt(a*a + b*b);
}
""" % tmpname)

dostuff = getattr(ROOT, tmpname)       # but assign to the same Python name
dostuff(3.14, 100)
1.0308919161099395

Towers of Hanoi (ハノイの塔)

このサイトからTowers of Hanoiコードを拝借する。

ROOT.gInterpreter.Declare("""
void moveDisks (int n, char fromPeg, char toPeg, char auxPeg) {

   if (n == 1) {                                           // stopping case
      cout << setw(6) << n
           << "            " << fromPeg
           << "            " << toPeg << endl;
   }
   else {                                                  // recursive step
      moveDisks(n-1, fromPeg, auxPeg, toPeg);
      cout << setw(6) << n
           << "            "  << fromPeg
           << "            " << toPeg << endl;
      moveDisks(n-1, auxPeg, toPeg, fromPeg);
   }
}
""")
True
ROOT.moveDisks(4, 'A', 'C', 'B')
     1            A            B
     2            A            C
     1            B            C
     3            A            B
     1            C            A
     2            C            B
     1            A            B
     4            A            C
     1            B            C
     2            B            A
     1            C            A
     3            B            C
     1            A            B
     2            A            C
     1            B            C

Pythonのprint関数を使って出力を参照サイトの出力と同じにする。

print("Move Disk #      From Peg     To Peg")
ROOT.moveDisks(4, 'A', 'C', 'B')
Move Disk #      From Peg     To Peg
     1            A            B
     2            A            C
     1            B            C
     3            A            B
     1            C            A
     2            C            B
     1            A            B
     4            A            C
     1            B            C
     2            B            A
     1            C            A
     3            B            C
     1            A            B
     2            A            C
     1            B            C

C++ magic %%cppを先頭行に付け足して以下のコードを実行すると、上と同じ結果が得られる。

%%cpp
cout << "Move Disk# From Peg To Peg" << endl;
moveDisks(4, 'A', 'C', 'B');
Move Disk# From Peg To Peg
     1            A            B
     2            A            C
     1            B            C
     3            A            B
     1            C            A
     2            C            B
     1            A            B
     4            A            C
     1            B            C
     2            B            A
     1            C            A
     3            B            C
     1            A            B
     2            A            C
     1            B            C

以上のように、%%cpp magicを使えば、C++の書式をそのまま使えるし、ROOT.gInterpreter.Declareを使えば、C++とPythonを一緒に使える。

cpp magicによる関数定義

%%cpp magicによる関数定義には%%cpp -dを使う。

%%cpp -d
int add (int a, int b)
{
  return a+b;
}
ROOT.add(2,4)
6
%%cpp
cout << add(2,4) << endl;
6
%%cpp
add(2,4)
(int) 6

ヘッダーファイルを使う

C++ header fileをPythonで使えるようにする。先ず最初にこのサイトからヘッダーファイルを引っ張ってくる。

#include "stdio.h"
class A{
    public:
    A(int i):m_i(i){}
    int getI() const {return m_i;}
    
    private:
    int m_i=0;
};

void printA(const A& a ){
    printf ("The value of A instance is %i.\n",a.getI());
}  
Writing cpp2pythonExample.h
ROOT.gInterpreter.ProcessLine('#include "cpp2pythonExample.h"')
a = ROOT.A(123)
ROOT.printA(a)
The value of A instance is 123.