Skip to content

Latest commit

 

History

History
2198 lines (1760 loc) · 63 KB

Root-cern-repl.org

File metadata and controls

2198 lines (1760 loc) · 63 KB

CPP / C++ Review

C++ CPP CERN - Root Cling Interpreter and tools

Overview

CERN’s (European Organization for Nuclear Research) Root Framework allows one to explore and play with C++ interactively through an interactive C++ interpreter where the user can type and evaluate expressions and commands in a similar way to Python’s REPL.

The Root interpreter, also known as CLING, currently can interpret C++14 and supports mostly U-nix like operating system like Linux or MacOSX and Windows support is still experimental.

Among other things, ROOT has the following features:

  • Run C++ code as scripts
  • Evaluate C++ code interactively
  • Load C libraries such as OpenGL, GNU scientific libraries
  • Load header only libraries such as Boost-Libraries
  • Data analysis tools and statistical libraries
  • Plotting tools

Possible Use-cases:

  • Learn C++ faster.
  • Exploratory design
  • Fast C++ prototyping
  • Explore and play with a wide range of C++ libraries
  • Test new ideas faster.
  • Learn about operating system APIs.

Note:

  • CLING is the ROOT’s C++ interpreter distributed in a standalone way without ROOT’s additional plotting and statistical libraries.

GIT Repository:

ROOT Forum:

Download:

Demonstration Videos:

Technical Explanations and talks:

  • Boostcon: Vassil Vassilev: Interactive, Introspected C++ at CERN - YouTube
    • “CERN is the world’s biggest particle physics laboratory. It has to process about 15 PB/year in order to make such scientific breakthroughs. The ROOT Framework’s unique abilities enable physicists to analyse that data with high efficiency, computationally and storage-wise. In the core of ROOT’s newest version is Cling – an interactive C++ interpreter, that targets support of C++11. Cling replaces a previous C++ interpreter that is traditionally the main user interface to ROOT. Cling is built on LLVM/Clang compiler infrastructure. The interpreter is not only used by ROOT to serialize, deserialize and manipulate the C++ OO representation of data, but to help novices learn and understand C++ faster.”
  • GoogleTechTalks: Introducing cling, a C++ Interpreter Based on clang/LLVM - YouTube
    • Presented by Axel Naumann, CERN. “At CERN, 50 million lines of C++ code are being used by about 10 thousand physicist. Many of them are not programming experts. To make writing C++ more accessible, ROOT (http://root.cern.ch), one of the core tools at CERN, has been using the CINT C++ interpreter for more than 15 years. CINT also opens up a whole new world of dynamic programming: plug-ins, signal/slot, runtime evaluation, reflection. In particular, the latter is fundamental to CERN and its petabytes of Large Hadron Collider data per year, which are created, serialized, and analyzed as C++ objects …”

Installation and configuration

Install from source

Compilation Instructions:

# Clone the repository 
$ git clone http://github.com/root-project/root.git

# Create build directory for compiling 
$ mkdir -p root/build && cd root/build

# Install program at $HOME/opt/cern-root or ~/opt/cern-root
$ cmake .. -Dcxx=17 -Dgviz=OFF -Dhdfs=OFF -Dkrb5=OFF -Dladp=OFF -Dmysql=OFF  -Dodbc=OFF \
  -Doracle=OFF -Dpgsql=OFF -Dx11=OFF -Dpython=OFF -Dbounjour=OFF \
  -DCMAKE_INSTALL_PREFIX=$HOME/opt/cern-root

# Compile with -jN where N is the number of processor cores.
$ make -j4 && make install

# Run ROOT REPL:
$ /home/archbox/opt/cern-root/bin/root.exe 
   ------------------------------------------------------------
  | Welcome to ROOT 6.14/04                http://root.cern.ch |
  |                               (c) 1995-2018, The ROOT Team |
  | Built for linuxx8664gcc                                    |
  | From tags/v6-14-04@v6-14-04, Aug 23 2018, 17:00:44         |
  | Try '.help', '.demo', '.license', '.credits', '.quit'/'.q' |
   ------------------------------------------------------------

Install from Docker

Docker Image:

Pull docker Image:

$ docker pull mazurov/cern-root

Run Shell:

$ docker run -t -i -a stdout -a stdin mazurov/cern-root

[root@4b6810a8ca0b /]# ls /opt/root/bin/    
genreflex           rmkdepend     rooteventselector  setenvwrap.csh
hadd                root          rootls             setxrd.csh
hist2workspace      root-config   rootmkdir          setxrd.sh
memprobe            root.exe      rootmv             ssh2rpd
pq2                 rootbrowse    rootn.exe          thisroot.csh
prepareHistFactory  rootcint      rootnb.exe         thisroot.sh
proofd              rootcling     rootprint          xpdtest
proofexecv          rootcp        rootrm
proofserv           rootd         roots
proofserv.exe       rootdrawtree  roots.exe
[root@4b6810a8ca0b /]# 
[root@4b6810a8ca0b /]# 

Run ROOT REPL:

$ docker run -t -i -a stdout -a stdin --entrypoint "root.exe" mazurov/cern-root 

Session:

  ------------------------------------------------------------
  | Welcome to ROOT 6.09/02                http://root.cern.ch |
  |                               (c) 1995-2016, The ROOT Team |
  | Built for linuxx8664gcc                                    |
  | From tag v6-09-02, 8 March 2017                            |
  | Try '.help', '.demo', '.license', '.credits', '.quit'/'.q' |
   ------------------------------------------------------------

root [0] 
root [0] .q
(base) 

root [0] std::cout << "Hello world C++11" << "\n";
Hello world C++11
root [1] 

Mount disk directory on docker image

$ docker run -it --rm -w /user -v $(pwd):/user --entrypoint="root.exe" mazurov/cern-root

Commands:

  • -w /user
    • Set Docker current directory to user
  • -v $(pwd):/user
    • Mount current disk directory into the directory /user in the Docker image.
  • –entrypoint=”/opt/root/bin/root.exe”
    • Set entry point to ROOT REPL.

Session Example:

$ docker run -it --rm -w /user -v $(pwd):/user --entrypoint="root.exe" mazurov/cern-root

 root [5] .! ls
 arraysFun.cpp			    object-lifecycle.cpp
 assert.cpp			    operator-overload1.bin
 basic-io.cpp			    operator-overload1.cpp
 boost				    operator-overload2.bin
 cpp-functor.cpp			    operator-overload2.cpp
 ... ...    ... ...    ... ...    ... ...    ... ...    ... ... 

 # Load File and run function main()
 root [6] .L numeric-limits.cpp 
 root [7] 
 root [7] main()
 Numeric limits for type: bool
 ------------------------------------------------------------
                          Type:                     bool
                    Is integer:                     true
                     Is signed:                    false
           Number of digits 10:                        0
       Max Number of digits 10:                        0
                       Min Abs:                        0
                           Min:                        0
                           Max:                        1
                 Size in bytes:                        1
                  Size in bits:                        8
 ...    ...    ...    ...    ...    ...    ...    ... 

Bash Function for usage simplification:

function docker-run(){
   DIMAGE="$1"
   if [ -n "$2" ] ; then 
      ENTRYPOINT="$2"
   else 
      ENTRYPOINT="/bin/sh"
   fi 
   docker run -it --rm -w /user -v $(pwd):/user --entrypoint $ENTRYPOINT $DIMAGE 
}
 
# Run shell (default entry-point)
$ docker-run mazurov/cern-root

# Run root.exe entry-point of Docker image (mazurov/cern-root)
$ docker-run mazurov/cern-root root.exe

Configuration

Set environment variables:

  • Add this piece of code to any of those configuration files: ~/.profile, ~/.bash_profile or ~/.bashrc.
# Set root directory (ROOTSYS) to the path where it was installed 
export ROOTSYS=$HOME/opt/root 
# DO NOT change those variables below 
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$ROOTSYS/lib 
export PATH=$PATH:$ROOTSYS/bin
alias cern-root="$ROOTSYS/bin/root -l"

Questions about configuration files:

Current install:

$ which root
/home/archbox/opt/root/bin/root

$ pwd
/home/archbox/opt/root

archbox@localhost 16:10 ~/opt/root
$ tree -L 1 .
.
├── aclocal
├── bin
├── cmake
├── config
├── emacs
├── etc
├── fonts
├── geom
├── icons
├── include
├── lib
├── LICENSE
├── macros
├── man
├── README
├── test
├── tmva
└── tutorials

17 directories, 1 file

Show tools available:

$ tree -L 1 bin/
bin/
├── g2root
├── genreflex
├── h2root
├── hadd
├── hist2workspace
├── memprobe
├── pq2
├── prepareHistFactory
├── proofd
├── proofexecv
├── proofserv
├── proofserv.exe
├── rmkdepend
├── root
├── rootbrowse
├── rootcint
├── rootcling
├── root-config
├── rootcp
├── rootd
├── rootdrawtree
├── rooteventselector
├── root.exe
├── rootls
├── rootmkdir
├── rootmv
├── rootnb.exe
├── rootn.exe
├── rootprint
├── rootrm
├── roots
├── roots.exe
├── rootslimtree
├── setenvwrap.csh
├── setxrd.csh
├── setxrd.sh
├── ssh2rpd
├── thisroot.csh
├── thisroot.sh
├── xpdtest
└── xproofd

0 directories, 41 files

Command Sumamry

REPL CommandDescription
.?Show help
.qExit ROOT shell
.L file.cppLoad file.cpp, so it loads all the file’s classes and functions
.x script.cxxLoad and execute ROOT script or C++ ordinary source code. The entry point is void script()
.includeShow include path
.I <include path>Add include path to search for header files (*.h), for instance .I usr/include/qt5
.! <shell command>Run shell command such as ls on Unix.
.class TFileShow all methods and fields of the class TFile

Documentation:

GSystem object:

gSystem->AddLinkedLibs (...) 
gSystem->AddIncludePath(...)

gROOT->GetListOfClasses()
gROOT->GetListOfColors()
gROOT->GetListOfTypes()
gROOT->GetListOfGlobals()
gROOT->GetListOfGlobalFunctions()
gROOT->GetListOfFiles()
gROOT->GetListOfMappedFiles()
gROOT->GetListOfSockets()
gROOT->GetListOfCanvases()
gROOT->GetListOfStyles()
gROOT->GetListOfFunctions()
gROOT->GetListOfSpecials()
gROOT->GetListOfGeometries()
gROOT->GetListOfBrowsers()
gROOT->GetListOfMessageHandlers()

Get Version:

root [20] gROOT->GetVersion()
(const char *) "6.14/04"
root [21]

Get and Set Prompt:

root [0] static_cast<TRint*>(gROOT->GetApplication())->GetPrompt()
(char *) "root [1] "
root [1]

root [1] static_cast<TRint*>(gROOT->GetApplication())->SetPrompt(">> ")
(const char *) "root [%d] "
>>
>>

Change and check current working directory.

root [30] gSystem->cd("/home/archbox")
(bool) true

root [31] gSystem->pwd()
(const char *) "/home/archbox"
root [32] 
root [32] 

Get environment variables:

root [32] gSystem->Getenv("HOME")
(const char *) "/home/archbox"

root [33] gSystem->Getenv("PATH")
(const char *) "/usr/lib64/qt-3.3/bin:/usr/local/bin:/usr/bin:/bin:..."

Add Include Path:

– Ref: Setting .include path in .rootrc file? - ROOT - ROOT Forum

gSystem->SetIncludePath(" -Imyincludepath1 ");
gSystem->SetIncludePath(" -Imyincludepath2 ");
...

Eval String:

root [0] gROOT->ProcessLine("std::cout << \"Hello world\" << std::endl;");
Hello world
root [1] 

root [2] gROOT->ProcessLine("cos(M_PI)");
(double) -1.0000000

root [3] gROOT->ProcessLine("cos(2 * M_PI)");
(double) 1.0000000
root [4] 

Print configuration:

  • Command: gEnv->Print()
Root [5] gEnv->Print()
Unix.*.Root.UseTTFonts:   true                           [Global]
WinNT.UseNetAPI:          true                           [Global]
Unix.*.Root.UseThreads:   false                          [Global]
Root.CompressionAlgorithm: 0                              [Global]
Root.ShowPath:            false                          [Global]
Root.TMemStat:            0                              [Global]
Root.TMemStat.buffersize: 100000                         [Global]
Root.TMemStat.maxcalls:   5000000                        [Global]
Root.TMemStat.system:                                    [Global]
Root.MemStat:             0                              [Global]
Root.MemStat.size:        -1                             [Global]
Root.MemStat.cnt:         -1                             [Global]
Root.ObjectStat:          0                              [Global]
Root.MemCheck:            0                              [Global]

Playing with Root REPL

Start root interpreter

$ $HOME/opt/root/bin/root 
ERROR in cling::CIFactory::createCI(): cannot extract standard library include paths!
Invoking:
  LC_ALL=C ccache  -O3 -DNDEBUG -xc++ -E -v /dev/null 2>&1 >/dev/null | awk '/^#include </,/^End of search/{if (!/^#include </ && !/^End of search/){ print }}' | GREP_OPTIONS= grep -E "(c|g)\+\+"
Results was:
With exit code 256
   ------------------------------------------------------------
  | Welcome to ROOT 6.14/04                http://root.cern.ch |
  |                               (c) 1995-2018, The ROOT Team |
  | Built for linuxx8664gcc                                    |
  | From tags/v6-14-04@v6-14-04, Aug 23 2018, 17:00:44         |
  | Try '.help', '.demo', '.license', '.credits', '.quit'/'.q' |
   ------------------------------------------------------------

root [0] 

Run shell command:

root [66] .! ls
a.out		 clang1.cpp	clang-start.bin   myclass.cpp	    testclang.bin
cashFlowApp.cpp  clangcpp1.bin	clang-start.cpp   myclass.hpp	    testclang.cpp
cashflow.cpp	 clangcpp1.cpp	diagnostics.bin   numLimits.cpp     testcl.bin
cashflow.h	 clanger.bin	diagnostics.cpp   printHeaders.cpp  testcl.cpp
cashflow.so	 clanger.c	dump-classes.cpp  source-info.bin
clang1.bin	 clanger.cpp	libcashflow.cpp   source-info.cpp
root [67]

root [67] .! pwd
/home/archbox/shared/reflection-root
root [68] 

Show Math constants

root [5] M_PI
(double) 3.1415927
root [6] M_E
(double) 2.7182818
root [7] 
root [7] // Predefined math constants in the header cmath
root [8] M_E
(double) 2.7182818
root [9] M_PI
(double) 3.1415927
root [10] M_LOG10E // Logarithm to base 2 of E
(double) 0.43429448
root [11] M_LN10 // Natural log of 10
(double) 2.3025851
root [12] M_PI_4 // PI divided by 4 or PI/4
(double) 0.78539816
root [13] M_2_PI // 2 * PI or 360 deg
(double) 0.63661977
root [14] M_SQRT2 // Square root of 2
(double) 1.4142136
root [15] M_SQRT1_2
(double) 0.70710678
root [16] 

Print to stdout

root [20] std::cout << "Hello world" << std::endl;
Hello world
root [21] 

root [21] for(int i = 0 ; i < 10; i++){ std::cout << "i = " << i << std::endl; }
i = 0
i = 1
i = 2
i = 3
i = 4
i = 5
i = 6
i = 7
i = 8
i = 9

Paste multiline

// To paste a multi line code, paste the code between brackets
  // To paste a multi line code, paste the code between brackets
  {
  auto func = [](double x){
      return x * x - 4 * x + 10;
  };
  }

  root [38] func(4.0)
  (double) 10.000000
  root [39] 
  root [39] func(0)
  (double) 10.000000
  root [40] func(3)
  (double) 7.0000000
  root [41] func(5)
  (double) 15.000000
  root [42] func(10)
  (double) 70.000000
  root [43] 

Playing with STL Vectors

root [47] std::vector<double> ys {10.0, 3.0, 5.0, 6.0, 10.0, 20.0}
(std::vector<double> &) { 10.000000, 3.0000000, 5.0000000, 6.0000000, 10.000000, 20.000000 }
root [48] 

root [48] ys.size()
(unsigned long) 6
root [49] ys.max_size()
(unsigned long) 2305843009213693951
root [50] ys[0]
(double) 10.000000
root [51] ys[1]
(double) 3.0000000
root [52] ys[2]
(double) 5.0000000
root [53] ys.at(0)
(double) 10.000000
root [54] ys.at(1)
(double) 3.0000000
root [55] ys.at(2)
(double) 5.0000000
root [56] ys.at(100)
Error in <TRint::HandleTermInput()>: std::out_of_range caught: vector::_M_range_check: __n (which is 100) >= this->size() (which is 6)
root [57] 

root [58] ys.push_back(5)
root [59] ys
(std::vector<double> &) { 10.000000, 3.0000000, 5.0000000, 6.0000000, 10.000000, 20.000000, 5.0000000 }
root [60] 

Playing with Deque - Double Ended Queue STL Container

root [71] std::deque<double> d;
root [72] d
(std::deque<double> &) {}

root [73] d. // Type tab to complete 
assign
at
back
begin
cbegin
cend
clear
crbegin
crend
... ... 

root [73] d.push_back(10.0)
root [74] d.push_back(3.0)
root [75] d.push_back(5.0)
root [76] d
(std::deque<double> &) { 10.000000, 3.0000000, 5.0000000 }
root [77] 

root [83] std::cout << std::fixed << std::setprecision(2)
(std::basic_ostream<char, std::char_traits<char> > &) @0x7fe94fd0ae20
root [84] 

// C++ 11 For-range based loop 
root [89] for(const auto& x: d){ std::cout << x << std::endl; }
10.00
6.00
10.00
3.00
5.00
root [90] 


root [88] for(const auto& x: d){ std::cout << std::right << std::setw(10) << x << std::end   
     10.00
      6.00
     10.00
      3.00
      5.00
root [89] 

// Clear 
root [97] d.clear()
root [98] d
(std::deque<double> &) {}
root [99] 

Playing with STL Maps

STL Map (dictionary, hash map) container:

// Create a map container with uniform initialization 
root [1] std::map<std::string, double> constants {{"pi", 3.1415}, {"earth_gravity", 9.81},(std::map<std::string, double> &) 
        { "earth_gravity" => 9.8100000, "pi" => 3.1415000, "sqrt_2" => 1.4170000 }
root [2] 
root [2] 

root [5] constants["earth_gravity"]
(double) 9.8100000
root [6] 
root [6] constants.at("earth_gravity")
(double) 9.8100000

// Generate exception 
root [7] constants.at("pi")
(double) 3.1415000
root [8] constants.at("pix")
Error in <TRint::HandleTermInput()>: std::out_of_range caught: map::at
root [9] 

root [9] constants.size()
(unsigned long) 4
root [10] 
root [10] 

root [11] constants.clear()
root [12] 
root [12] constants
(std::map<std::string, double> &) {}
root [13] 

root [15] constants.insert(std::pair<std::string, double>("pi", 3.1415))
root [17] constants.insert(std::pair<std::string, double>("x", 10.0))

root [18] constants
(std::map<std::string, double> &) { "pi" => 3.1415000, "x" => 10.000000 }
root [19] 

{
for(const auto& x: constants){
        cout << "key   = " << std::setw(4) << x.first << std::setw(10)
             << "value = " << x.second << endl;
    }
}
// Output 
key   =   pi  value = 3.1415
key   =    x  value = 10

Playing with classes

CashFlow class

ROOT Cling can also play with C++ classes as they were ordinary scripts.

File: CashFlow.cpp

#include <iostream>
#include <vector>
#include <initializer_list>
#include <iomanip> // setw, setpreicision ...

class CashFlow{
private:
   std::vector<double> m_pmt;
public:
    // Default constructor - doesn't
    CashFlow(){}

    // Overloaded contructor with vector
    CashFlow(std::vector<double> pmt){
        m_pmt.insert(m_pmt.begin(), pmt.begin(), pmt.end());
    }
    // Overloaded constructor with initializer list
    CashFlow(std::initializer_list<double> pmt){
        m_pmt.insert(m_pmt.begin(), pmt.begin(), pmt.end());
    }
    CashFlow& add(double x){
        m_pmt.push_back(x);
        return *this;
    }
    void show(){
        int i = 0;
        for(const auto& x: m_pmt){
            std::cout << std::setw(10) << i
                      << std::setw(10) << std::setprecision(3) << std::fixed << x
                      << std::endl;
            ++i;
        }
    }

};

In the ROOT shell:

root [0] .L CashFlow.cpp 

root [1] CashFlow clf;

root [2] clf.show()

root [3] clf.add(-30).add(20).add(4).add(5).add(25)
(CashFlow &) @0x7fa4df246010
root [4] clf.show()
         0   -30.000
         1    20.000
         2     4.000
         3     5.000
         4    25.000

root [6] 
root [6] CashFlow clf2 {-30.0, 20.0, 3.0, 5.0, 25.0} ;
root [7] clf2.show()
         0   -30.000
         1    20.000
         2     3.000
         3     5.000
         4    25.000
root [8] 

Linear function class

ROOT Session:

root [0] .L linfun.cpp 
root [1] 
root [1] LinearFunction lfun1(3.0, 4.0)
(LinearFunction &) @0x7fac4d729010
root [2] lfun1
(LinearFunction &) @0x7fac4d729010
root [3] std::cout << lfun1 << std::endl;
y(x) = 3.000 * x + 4.000
root [4] 
root [4] lfun1(3.0)
(double) 13.000000
root [5] lfun1(0)
(double) 4.0000000
root [6] lfun1(5)
(double) 19.000000
root [7] lfun1(10)
(double) 34.000000
root [8] lfun1.setCoeffs(5.0, 10.0);
root [9] 
root [9] std::cout << lfun1 << std::endl;
y(x) = 5.000 * x + 10.000
root [10] 
root [10] std::vector<double> xs{3.0, 4.0, 5.0, 6.0, 5.0};
root [11] 
root [11] xs
(std::vector<double> &) { 3.0000000, 4.0000000, 5.0000000, 6.0000000, 5.0000000 }
root [12] 
root [12] lfun1(xs)
(std::vector<double>) { 25.000000, 30.000000, 35.000000, 40.000000, 35.000000 }
root [13] 
root [13] auto lfun2 = LinearFunction::fromPoints(2, 9, 8 , 21);
root [14] std::cout << lfun2 << std::endl;
y(x) = 2.000 * x + 5.000
root [15] 
root [15] lfun2(3.0) 
(double) 11.000000
root [16] lfun2(4.0) 
(double) 13.000000
root [17] lfun2(5.0) 
(double) 15.000000
root [18] 

File: linfun.cpp

class LinearFunction{
public:
    LinearFunction(double a, double b): A(a), B(b) {}

    /* Named constructor, aka static factory method*/
    static LinearFunction fromCoeffs(double a, double b){
        return LinearFunction(a, b);
    }   
    /* Named constructor, aka static factory method*/
    static LinearFunction fromPoints(double x1, double y1, double x2, double y2){
        double a = (y2 - y1)/(x2 - x1);
        double b = y1 - a * x1;
        return LinearFunction(a, b);
    }

    double eval(double x){
        return A * x + B;
    }

    // Function-call-operator overload
    // Using the New C++11 return type
    // It could also be:
    //  >> double operator()(double x){ ... 
    auto operator()(double x) -> double{
        return A * x + B;
    }   
    // Function-call-operator overload
    std::vector<double> operator()(const std::vector<double>& xs){
        std::vector<double> res;
        for(auto& x: xs){
            res.push_back(A * x + B);
        }
        return res;
    }   
    void setCoeffs(double A, double B){
        this->A = A;
        this->B = B;
    }
    void setA(double a){
        A = a;
    }
    void setB(double b){
        B = b;
    }
    // The stream insertion operator (<<) is not a method 
    // (member function) of this class. It is a overload of 
    // the operator (<<) for the class std::ostream which is
    // a generic output stream.
    friend std::ostream& operator<<(std::ostream &os, const LinearFunction& lfun){
        os.precision(3);
        os.setf(std::ios::fixed);
        os << "y(x) = " << lfun.A << " * x" << " + " << lfun.B;
        return os;
    }
private:
    double A;
    double B;
}; //---- End of object LinearFunction --- //

/** Makes the vector printable, similar to implementing vector.toString in Java */
std::ostream& operator << (std::ostream &os, const std::vector<double>& xs){
    os << "[" << xs.size() << "](" ;
    copy(xs.begin(), xs.end(), std::ostream_iterator<double>(os, " "));
    os << ")";
    return os;
}

Playing with higher order functions and C++11 lambdas

To load the following code, just copy and then paste it in the ROOT REPL.

// Type synonym to avoid repeating it.
// Equivalent to typedef std::function<double (double)> MathFun 
using MathFun = std::function<double (double)>;

/** Higher order function to tabulate ordinary function 
  * The first parameter can be a ordinary lambda function or 
  * a any function object implementing  double operator()(double x)
  * or  operator()(double) => double Using Scala's notation.
  */
void tabulate(
    std::function<double (double)> fn,
    double start,
    double stop,
    double step,
    std::ostream& os = std::cout
    ){      
    os.precision(3);
    os.flags(std::ios::fixed);
    os << std::setw(10) << "Input" << std::setw(10) << "Output" << std::endl;
    double x = start;
    while(x <= stop){
        os << std::setw(10) << x << std::setw(10) << fn(x) << std::endl;
        x = x + step;
    }
}

Running:

root [40] tabulate([](double x){ return sqrt(x);}, -4.0, 9.0, 1.0)
     Input    Output
    -4.000      -nan
    -3.000      -nan
    -2.000      -nan
    -1.000      -nan
     0.000     0.000
     1.000     1.000
     2.000     1.414
     3.000     1.732
     4.000     2.000
     5.000     2.236
     6.000     2.449
     7.000     2.646
     8.000     2.828
     9.000     3.000
root [41] 

MathFun makeLinFun(double a, double b)  {
    // [=] means -> capture a and b by value 
    return [=](double x){return a * x + b; };
}

root [70] tabulate(makeLinFun(10.0, 5.0), -5, 5, 1)
     Input    Output
    -5.000   -45.000
    -4.000   -35.000
    -3.000   -25.000
    -2.000   -15.000
    -1.000    -5.000
     0.000     5.000
     1.000    15.000
     2.000    25.000
     3.000    35.000
     4.000    45.000
     5.000    55.000
root [71] 

Playing with STL algorithms

Required headers: <iostram> and <algorithm> (std::for_each)

  • C Arrays
root [0] double xs [] = {10.0, 5.0, 6.0, 3.0}
(double [4]) { 10.000000, 5.0000000, 6.0000000, 3.0000000 }
root [1] 
root [1] std::for_each(xs, xs + 4, [](double x){ std::cout << sqrt(x) << " " << '\n' << std::flush;} );
3.16228 
2.23607 
2.44949 
1.73205 
root [2] 
  • C++ Vectors
root [0] std::vector<double> vec { 10.0, 3.0, 5.0, 2.0, -6.0} ;
root [1] xs

root [3] std::for_each(vec.begin(), vec.end(), [](double x){ std::cout << sqrt(x) << std::endl;})
3.16228
1.73205
2.23607
1.41421
-nan
((lambda)) @0x1a42030

root [4] std::for_each(vec.begin(), vec.end(), [](double x){ std::cout << sqrt(x) << std::endl;});
3.16228
1.73205
2.23607
1.41421
-nan

Show a file

Paste the following code in the ROOT interpreter.

{
// Headers:  <iostream>, <fstream>,  <stdlib.h>
void showFile(const char* file){
  std::ifstream fin;
  std::string line;
  fin.open(file);
  if(fin.fail()){
    std::cerr << "Error: file " << file << " cannot be opened.";
    exit(-1);
  }
  while(!fin.eof()){
    std::getline(fin, line);
    std::cout << line << std::endl;
  }
  fin.close();
}

}

Run:

root [24] showFile("/etc/protocols")
# /etc/protocols:
# $Id: protocols,v 1.12 2016/07/08 12:27 ovasik Exp $
#
# Internet (IP) protocols
#
#	from: @(#)protocols	5.1 (Berkeley) 4/17/89
#
# Updated for NetBSD based on RFC 1340, Assigned Numbers (July 1992).
# Last IANA update included dated 2011-05-03
#
# See also http://www.iana.org/assignments/protocol-numbers

ip	0	IP		# internet protocol, pseudo protocol number
hopopt	0	HOPOPT		# hop-by-hop options for ipv6
icmp	1	ICMP		# internet control message protocol
igmp	2	IGMP		# internet group management protocol
ggp	3	GGP		# gateway-gateway protocol
ipv4	4	IPv4		# IPv4 encapsulation
 ... ...  ... ...  ... ...  ... ...  ... ...  ... ... 

Using boost libraries

It assumes that the boost libraries are already installed.

root [0] #include <boost/math/special_functions/erf.hpp>
root [1] boost::math::erf

root [2] boost::math::erf(0.1)
(double) 0.11246292
root [3] 
root [3] boost::math::erf(2.0)
(double) 0.99532227
root [4] boost::math::erf(3.0)
(double) 0.99997791
 
root [9] using boost::math::erf;
root [10] 
root [10] erf(1.2)
(double) 0.91031398
root [11] erf(4.5)
(double) 1.0000000
root [12] 

Playing with GNU Scientific library shared library

Note: the command #pragma cling load(“/lib64/libgslcblas.so.0”) is used to load the symbols from the shared library libgslcblas.so.

root [1] #pragma cling load("/lib64/libgslcblas.so.0")
root [2] 
root [2] #pragma cling load("/lib64/libgsl.so")
root [3] 
root [3] #include <gsl/gsl_errno.h>
root [4] #include <gsl/gsl_sf_bessel.h>
root [5] 
root [5] gsl_sf_bessel_J0(4.0)
(double) -0.39714981
root [6] gsl_sf_bessel_J0(5.0)
(double) -0.17759677
root [7] 

{
  double x = 5.0;
  double expected = -0.17759677131433830434739701;

  double y = gsl_sf_bessel_J0 (x);

  printf ("J0(5.0) = %.18f\n", y);
  printf ("exact   = %.18f\n", expected);
}
// Output: 
J0(5.0) = -0.177596771314338264
exact   = -0.177596771314338292
root [14] 

Complete script using (#prgram cling load) to load the shared libraries command:

#include <gsl/gsl_errno.h>
#include <gsl/gsl_sf_bessel.h>

#pragma cling load("/lib64/libgslcblas.so.0")
#pragma cling load("/lib64/libgsl.so")

gsl_sf_bessel_J0(4.0);
gsl_sf_bessel_J0(5.0);
double x = 5.0;
double expected = -0.17759677131433830434739701;

double y = gsl_sf_bessel_J0 (x);

printf ("J0(5.0) = %.18f\n", y);
printf ("exact   = %.18f\n", expected);

Complete script using gSystem->Load to add load libraries:

#include <gsl/gsl_errno.h>
#include <gsl/gsl_sf_bessel.h>

gSystem->Load("/lib64/libgslcblas.so.0");
gSystem->Load("/lib64/libgsl.so");

gsl_sf_bessel_J0(4.0);
gsl_sf_bessel_J0(5.0);
double x = 5.0;
double expected = -0.17759677131433830434739701;

double y = gsl_sf_bessel_J0 (x);

printf ("J0(5.0) = %.18f\n", y);
printf ("exact   = %.18f\n", expected);

Complete script using gSystem->AddLinkedLibs to load shared libraries:

#include <gsl/gsl_errno.h>
#include <gsl/gsl_sf_bessel.h>

gSystem->AddLinkedLibs("-lgsl -lgslcblas");
// gSystem->AddLinkedLibs("-lgsl");
// gSystem->AddLinkedLibs("-lgslcblas");

gsl_sf_bessel_J0(4.0);
gsl_sf_bessel_J0(5.0);
double x = 5.0;
double expected = -0.17759677131433830434739701;

double y = gsl_sf_bessel_J0 (x);

printf ("J0(5.0) = %.18f\n", y);
printf ("exact   = %.18f\n", expected);

Testing Unix System-Calls and APIs

Get current directory - getcwd()

Get current working directory of current process:

#include <unistd.h>

root [10] getcwd(nullptr, 0)
(char *) "/home/archbox/shared/reflection-root"
root [11] 

root [7] std::string current_dir = getcwd(nullptr, 0)
(std::string &) "/home/archbox/shared/reflection-root"

root [8] current_dir
(std::string &) "/home/archbox/shared/reflection-root"

root [9] std::cout << "Current directory = " << current_dir << std::endl;
Current directory = /home/archbox/shared/reflection-root

Set current working directory of current process:

oot [15] 
root [15] getcwd(nullptr, 0)
(char *) "/etc"
root [16] 
root [16] chdir("/usr/include")
(int) 0
root [17] getcwd(nullptr, 0)
(char *) "/usr/include"
root [18] chdir("/usr/includeError")
(int) -1
root [19] getcwd(nullptr, 0)
(char *) "/usr/include"
root [20] 

Create a directory - mkdir

Documentation:

root [9] #include <stdlib.h>
root [10] #include <limits.h>  
root [11] #include <unistd.h>
root [12] #include <sys/stat.h> 
root [13] 
root [14] mkdir("/home/archbox/Desktop/mydir", 0777)
(int) 0
root [15] 
root [15] mkdir("/home/archbox/Desktop/mydir", 0777)
(int) -1
root [16] 

List directory - opendir

// #include <string>
#include <sys/types.h>
#include <dirent.h>  // Get function opendir
#include <errno.h>

void listDirectory(const std::string& path){
    DIR *dir;
    struct dirent *dp;
    dir = opendir(path.c_str()) ;
    // To determine the cause of error - It is necessary to check the error code.
    if (dir == NULL)
     throw std::runtime_error("Error: Cannot read directory");
    while ((dp = readdir(dir)) != NULL) {
    std::cout << dp->d_name << std::endl ;
    };
    closedir(dir);
}

Running in root REPL:

root [37] listDirectory("/")
etc
tmp
sbin
sys
opt
media
.
boot
.local
.autorelabel
home
var
dev
.. ... ... 

root [39] listDirectory("/boot/grub")
.
splash.xpm.gz
..
root [40] 
root [40] 

root [40] listDirectory("/boot/grub/dsafa")
Error in <TRint::HandleTermInput()>: std::runtime_error caught: Error: Cannot read directory
root [41] 
root [41] 

Read process output with popen

// Copy and paste this code in the ROOT REPL
{
        FILE* fp = popen("ls -l /", "r");
        char ch;
        std::stringstream ss;
        if(!fp)
                std::cerr << "Error: could not open process output." << std::endl;
        while((ch = fgetc(fp)) != EOF){
                ss << ch;
        }
        pclose(fp);
        std::cout << "Output = " << '\n' << ss.str() << std::flush;	
}

// Ouptut: 

Output:

Output = 
total 64
lrwxrwxrwx.   1 root root     7 Feb 10  2017 bin -> usr/bin
dr-xr-xr-x.   7 root root  4096 Jul 15 15:58 boot
drwxr-xr-x   23 root root  4220 Sep  8 17:47 dev
drwxr-xr-x. 169 root root 12288 Sep 10 03:27 etc
drwxr-xr-x.   5 root root  4096 Mar  2  2018 home
lrwxrwxrwx.   1 root root     7 Feb 10  2017 lib -> usr/lib
lrwxrwxrwx.   1 root root     9 Feb 10  2017 lib64 -> usr/lib64
drwx------.   2 root root 16384 Sep 17  2017 lost+found
drwxr-xr-x.   2 root root  4096 Feb 10  2017 media
drwxr-xr-x.   2 root root  4096 Feb 10  2017 mnt
 ... .... ... .... ... .... ... .... ... .... 

This unnamed script can be encapsulate into a function:

std::string getProcessOutput(std::string command){
        FILE* fp = popen(command.c_str(), "r");
        char ch;
        std::stringstream ss;
        if(!fp)
                std::cerr << "Error: could not open process output." << std::endl;
        while((ch = fgetc(fp)) != EOF){
                ss << ch;
        }
        pclose(fp);
        return ss.str();
}

root [14] getProcessOutput("date")
(std::string) "Mon Sep 10 16:46:06 -03 2018
"
root [15] getProcessOutput("uname -a")
(std::string) "Linux localhost.localdomain 4.16.11-100.fc26.x86_64 #1 SMP Tue May 22 20:02:12 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
"
root [16] 

Run a script with shared library

C++ Script Examples

Example: Run a Ublas - Boost Library Linear Algebra C++ script

File: ublas.C

#include <iostream>
#include <string>

// Headers for vectors 
#include <boost/numeric/ublas/vector.hpp>
#include <boost/numeric/ublas/io.hpp>

// Headers for Matrix 
#include <boost/numeric/ublas/matrix.hpp>

namespace ub = boost::numeric::ublas;

template<class T>
void printVal(const std::string &name, const T &value){
    std::cout << name << " = " << value << std::endl;
}

void printSection(const std::string &descr){
    std::cout << descr << std::endl;
    for(int i = 0; i < descr.size(); i++){
        std::cout << '-';
    }
    std::cout << std::endl;
}

void vecOperation1(){
    ub::vector<double> vec1(5, 1.0);
    printVal("vec1", vec1);
    vec1[1] = 2.4;
    vec1[2] = 3.5;
    vec1[3] = -5.0;
    printVal("vec1", vec1); 
    printVal("sum(vec1)", sum(vec1));
    printVal("norm_1(vec1)", norm_1(vec1));
    printVal("norm_2(vec1)", norm_2(vec1)); 
    printVal("norm_inf(vec1)", norm_inf(vec1));
    printVal("index_norm_inf(vec1)", index_norm_inf(vec1)); 
}

void vecOperation2(){
    ub::vector<double> vec1(3, 2.2) ; vec1[2] = -5.1;
    ub::vector<double> vec2(3, -1.2); vec2[2] = 1.1;

    double factor = 2.5;

    printVal("vec1", vec1);
    printVal("vec1.size()", vec1.size());

    printVal("vec2", vec2);
    printVal("vec2.size()", vec2.size());

    printVal("inner_prod(vec1, vec2)", inner_prod(vec1, vec2));

    printVal("vec1 + vec2", vec1 + vec2);
    printVal("vec1 - vec2", vec1 - vec2);
    printVal("vec1 * factor", vec1 * factor);
    printVal("vec1 / factor", vec1 / factor);

}

void matrixOperation1(){
    ub::matrix<double> matrix1(3, 3, 2.5);
    matrix1(0, 0) = matrix1(2, 2) = 1.0;
    matrix1(0, 2) = -3.5; matrix1(2, 0) = 5.9;

    printVal("matrix1     ", matrix1);
    printVal("Num of rows ", matrix1.size1());
    printVal("Num of cols ", matrix1.size2());
    printVal("Transpose   ", trans(matrix1));
    printVal("Real part   ", real(matrix1));

    matrix1.resize(4, 4);
    printVal("Matrix resized = matrix1.resize(4, 4)", matrix1);

    printVal("identity_matrix<double>(3)", ub::identity_matrix<double>(3));
    printVal("zero_matrix<double>(3)", ub::zero_matrix<double>(3));

}

// SCRIPT Entry-point - must have the same name as the file.
void ublas(){
    printSection("Running vecOperation1");
    vecOperation1();
    std::cout << std::endl; 

    printSection("Running vecOperation2");
    vecOperation2();
    std::cout << std::endl;

    printSection("Running matrixOperation1");
    matrixOperation1();
}

Running in batch mode:

$ $HOME/opt/root/bin/root -l -q ublas.C
ERROR in cling::CIFactory::createCI(): cannot extract standard library include paths!
Invoking:
  LC_ALL=C ccache  -O3 -DNDEBUG -xc++ -E -v /dev/null 2>&1 >/dev/null | awk '/^#include </,/^End of search/{if (!/^#include </ && !/^End of search/){ print }}' | GREP_OPTIONS= grep -E "(c|g)\+\+"
Results was:
With exit code 256

Processing ublas.C...
Running vecOperation1
---------------------
vec1 = [5](1,1,1,1,1)
vec1 = [5](1,2.4,3.5,-5,1)
sum(vec1) = 2.9
norm_1(vec1) = 12.9
norm_2(vec1) = 6.70895
norm_inf(vec1) = 5
index_norm_inf(vec1) = 3

Running vecOperation2
---------------------
vec1 = [3](2.2,2.2,-5.1)
vec1.size() = 3
vec2 = [3](-1.2,-1.2,1.1)
vec2.size() = 3
inner_prod(vec1, vec2) = -10.89
vec1 + vec2 = [3](1,1,-4)
vec1 - vec2 = [3](3.4,3.4,-6.2)
vec1 * factor = [3](5.5,5.5,-12.75)
vec1 / factor = [3](0.88,0.88,-2.04)

Running matrixOperation1
------------------------
matrix1      = [3,3]((1,2.5,-3.5),(2.5,2.5,2.5),(5.9,2.5,1))
Num of rows  = 3
Num of cols  = 3
Transpose    = [3,3]((1,2.5,5.9),(2.5,2.5,2.5),(-3.5,2.5,1))
Real part    = [3,3]((1,2.5,-3.5),(2.5,2.5,2.5),(5.9,2.5,1))
Matrix resized = matrix1.resize(4, 4) = [4,4]((1,2.5,-3.5,2.93415e+59),(2.5,2.5,2.5,1.10542e+161),(5.9,2.5,1,9.83212e-72),(1.41746e+190,5.16752e+25,6.32283e+233,6.94321e-307))
identity_matrix<double>(3) = [3,3]((1,0,0),(0,1,0),(0,0,1))
zero_matrix<double>(3) = [3,3]((0,0,0),(0,0,0),(0,0,0))

Example: GNU Scientific Library C++ Wrapper Script

File: script1.C

/** 
 *  Reference: 
 *   + https://root-forum.cern.ch/t/loading-a-library-from-a-script/24306
 **/
#include <iostream>
#include <iomanip>
#include <functional>
#include <cmath>
#include <ostream>

// Install GNU Scientific Library on Fedora with:
// $ sudo dnf install gsl-devel.x86_64
#include <gsl/gsl_sf_bessel.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_roots.h>

// Load Shared libraries needed by the script
#ifdef  __CLING__
  R__LOAD_LIBRARY(/lib64/libgslcblas.so.0);
  R__LOAD_LIBRARY(/lib64/libgsl.so);
#endif 

namespace GSL{
        using MFun = std::function<double (double)>;
	
        double wrapLambda(double x, void* param){
                auto fp = static_cast<MFun*>(param);
                return fp->operator()(x);
        }

/**  
  * Note: In order to to not throw the error:  <ERROR: endpoints do not straddle y=0>
  * mf(xa) * mf(xb) < 0 the function must have opposite signals at the interval bounds.
  */
        double rootSolverBracket(
                // gsl_root_fsolver_type* solvertype,
                MFun   mf,
                double xa,
                double xb,
                int    maxIteratiosn = 100,
                double tol = 1e-5
                ){
                gsl_function fnt;
                fnt.function = wrapLambda;
                fnt.params   = &mf;

                gsl_root_fsolver *solver;
                solver = gsl_root_fsolver_alloc(gsl_root_fsolver_bisection);
                //solver = gsl_root_fsolver_alloc(solvertype);  
                // dbgtrace("Setting solver");
                gsl_root_fsolver_set(solver, &fnt, xa, xb);
                // dbgtrace("Solver set OK");
                // gsl_root_fsolver_iterate(solver);
                // dbgtrace("Solver initial iteration OK");
                int status = GSL_CONTINUE;
                int  i = 0;
                double root;
                double xlo, xhi;
                // dbgtrace("In the solver loop");
                while(i <= maxIteratiosn && status == GSL_CONTINUE){
                        // dbgtrace("Iterating solver");
                        status = gsl_root_fsolver_iterate(solver);
                        // disp(status);
                        if(status != GSL_SUCCESS)
                                break;
                        root = gsl_root_fsolver_root(solver);
                        // disp(root);
                        xlo = gsl_root_fsolver_x_lower(solver);
                        xhi = gsl_root_fsolver_x_upper(solver);
                        status = gsl_root_test_interval(xlo, xhi, 0, tol);
                        // if(status == GSL_SUCCESS)
                        //  std::cerr << "Converged" << std::endl;          
                        i++;
                }
                gsl_root_fsolver_free(solver);
                if(status == GSL_SUCCESS)
                        return root;
                else
                        return std::numeric_limits<double>::signaling_NaN();;   
                return 0;
        }
	
}

/** Equation "functor" - function-object */
struct EquationTest{
    double A;
    double B;
    EquationTest(double a, double b): A(a), B(b) {};
    /* Computes: A * x^2 - B * sin(x) */
    double operator()(double x){
        return A * x * x * x - B * sin(x);
    }
};
	
/** Script entry-point should have the same name as the file. */
void script1(){

  if(__cplusplus >= 201703L)
    std::cout << "Running C++17" << "\n";
  else if(__cplusplus >= 201402L)
    std::cout << "Running C++14" << "\n";
  else if(__cplusplus >= 201103L)
    std::cout << "Running C++11" << "\n";

  std::cout << "Running a C++ script in the ROOT REPL. " << "\n";

  std::cout << " gsl_sf_bessel_J0(4.0) = "
            << gsl_sf_bessel_J0(4.0) << "\n";

  double root = GSL::rootSolverBracket(
        // gsl_root_fsolver_bisection,
        [](double x){ return x * x - 25.0 ;}
        , -4, +10, 200
        );
   std::cout << "Root of equation x^2 - 25.0 =  " << root << "\n";      

   auto eqn = EquationTest(1.0, 4.0);  
   double root2 = GSL::rootSolverBracket(eqn, -10, +5, 200);
   std::cout << "Root of equation x^3 - 4 * sin(x) =  " << root2 << "\n"; 

} //--- End of script1 ---//

/** Main was added to allow compiling the script */
int main(){
        script1();
        return 0;
}

To run the script in the ROOT/CLING REPL use:

$ root 
>> .x script1.C
Running C++11
Running a C++ script in the ROOT REPL. 
 gsl_sf_bessel_J0(4.0) = -0.39715
Root of equation x^2 - 25.0 =  5.00001
Root of equation x^3 - 4 * sin(x) =  1.58732
>> 

Load the script, play and prototype with it.

>> .L script1.C
>> 
>> script1()
Running C++11
Running a C++ script in the ROOT REPL. 
 gsl_sf_bessel_J0(4.0) = -0.39715
Root of equation x^2 - 25.0 =  5.00001
Root of equation x^3 - 4 * sin(x) =  1.58732
>> 

>> GSL::
rootSolverBracket
wrapLambda
>> 
>> double x = 1.58732
(double) 1.5873200
>> pow(x, 3) - 4 * sin(x)
(double) -6.6632073e-05
>> 

>> GSL::rootSolverBracket([](double x){ return x * x * x - 4 * sin(x);}, -10, 5, 200)
(double) 1.5873218
>> 

Run the script in batch mode:

  • $ root -l -q script1.C
$ time cern-root -l -q script1.C

Processing script1.C...
Running C++11
Running a C++ script in the ROOT REPL. 
 gsl_sf_bessel_J0(4.0) = -0.39715
Root of equation x^2 - 25.0 =  5.00001
Root of equation x^3 - 4 * sin(x) =  1.58732

real	0m0.469s
user	0m0.335s
sys	0m0.096s

Compile the script:

$ clang++ -std=c++1z script1.C -o script1.bin   -Wall -Wextra -g -lgsl -lgslcblas 

$ ./script1.bin 
Running C++17
Running a C++ script in the ROOT REPL. 
 gsl_sf_bessel_J0(4.0) = -0.39715
Root of equation x^2 - 25.0 =  5.00001
Root of equation x^3 - 4 * sin(x) =  1.58732

How to extract the shared libraries needed to run the program?

$ ldd script1.bin 
        linux-vdso.so.1 (0x00007ffe0f5b8000)
        libgsl.so.23 => /lib64/libgsl.so.23 (0x00007fcae26ca000)
        libgslcblas.so.0 => /lib64/libgslcblas.so.0 (0x00007fcae248b000)
        libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007fcae20f9000)
        libm.so.6 => /lib64/libm.so.6 (0x00007fcae1d65000)
        libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007fcae1b4d000)
        libc.so.6 => /lib64/libc.so.6 (0x00007fcae178e000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fcae2b39000)

Load Python3 (CPython) C-API in the REPL

Install Python 3 development libraries: (Fedora Linux)

$ sudo dnf install python3-devel.x86_64

Locate where is Python3 shared library:

$ ldd $(which python3)
        linux-vdso.so.1 (0x00007fff82b64000)
        libpython3.6m.so.1.0 => /lib64/libpython3.6m.so.1.0 (0x00007fd1eb4a5000)
        libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fd1eb286000)
        libdl.so.2 => /lib64/libdl.so.2 (0x00007fd1eb082000)
        libutil.so.1 => /lib64/libutil.so.1 (0x00007fd1eae7f000)
        libm.so.6 => /lib64/libm.so.6 (0x00007fd1eaaeb000)
        libc.so.6 => /lib64/libc.so.6 (0x00007fd1ea72c000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fd1ebc04000)

 $ ldd $(which python3) | grep python
         libpython3.6m.so.1.0 => /lib64/libpython3.6m.so.1.0 (0x00007ff0015ee000)

Locate Python 3 header files:

$ find /usr/include/ -name "Python.h"
/usr/include/python3.6m/Python.h

Start ROOT REPL:

$ cern-root
 ==> Starting root configuration from: /home/archbox/.rootalias.C
Current dir = /home/archbox/Documents/projects/cpp-programming.cpp/src
(std::basic_ostream<char, std::char_traits<char> >::__ostream_type &) @0x7fb42fc7eec0

Load the Python3 shared library and its header file:

>> #include "python3.6m/Python.h"
>> #pragma cling load("/lib64/libpython3.6m.so.1.0")

Initialize Python3:

>> Py_Initialize();

Python C-API Py_GetPath():

>> Py_GetPath()
(wchar_t *) @0x7fff9a144968

>> std::wcout << "path = " << Py_GetPath() << L"\n";
path = /usr/lib64/python36.zip:/usr/lib64/python3.6:/usr/lib64/python3.6:/usr/lib64/python3.6/lib-dynload
>> 

Get Version:

>> Py_GetVersion()
(const char *) "3.6.7 (default, Nov 23 2018, 12:11:28) 
[GCC 8.2.1 20181105 (Red Hat 8.2.1-5)]"

Get Platform:

>> Py_GetPlatform()
(const char *) "linux"

Information about Python interpreter:

>> Py_GetPrefix()
(wchar_t *) @0x7fff9a144968

>> std::wcout << L" => Py_GetPrefix() = " << Py_GetPrefix() << L"\n";
 => Py_GetPrefix() = /usr

>> Py_GetProgramFullPath()
(wchar_t *) @0x7fff9a144968

>> std::wcout << L" => Py_GetProgramFullPath() = " << Py_GetProgramFullPath() << L"\n";
 => Py_GetProgramFullPath() = /usr/bin/python3

Get copyright banner:

>> Py_GetCopyright()
(const char *) "Copyright (c) 2001-2018 Python Software Foundation.
All Rights Reserved.

Copyright (c) 2000 BeOpen.com.
All Rights Reserved.

Copyright (c) 1995-2001 Corporation for National Research Initiatives.
All Rights Reserved.

Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam.
All Rights Reserved."
>> 

Python C-API PyRun_SimpleString:

>> PyRun_SimpleString("print(' ==>> Hello world Python3')")
 ==>> Hello world Python3
(int) 0
>> 

>> PyRun_SimpleString("print(m.sin(m.pi))")
1.2246467991473532e-16
(int) 0

>> PyRun_SimpleString("print(m.cos(m.pi))")
-1.0
(int) 0

>> PyRun_SimpleString("print(m.exp(2.5))")
12.182493960703473
(int) 0

>> PyRun_SimpleString("print(wrong(2.5))")
Traceback (most recent call last):
  File "<string>", line 1, in <module>
NameError: name 'wrong' is not defined
(int) -1

Creating primitive python float point object:

>> PyObject* value = Py_BuildValue("f", 5.136);

>> value
(PyObject *) 0x7fb43059f240

>> PyObject_Print(value, stdout, 0), std::cout << std::endl;
5.136

Creating primitive python string object:

>> PyObject* pystring = Py_BuildValue("s", " Python3-C++-REPL");

>> PyObject_Print(pystring, stdout, 0), std::cout << std::endl;
' Python3-C++-REPL'

Importing module sys:

>> PyObject* msys = PyImport_ImportModule("sys")
(PyObject *) 0x7fb430593ef8

>> PyObject* pyver = PyObject_GetAttrString(msys, "version")
(PyObject *) 0x7fb4305970b0
>> 

>> PyObject_Print(pyver, stdout, 0), std::cout << std::endl;
'3.6.7 (default, Nov 23 2018, 12:11:28) \n[GCC 8.2.1 20181105 (Red Hat 8.2.1-5)]'
>> 

Importing module math:

// Equivalen to: import math 
>> PyObject* m = PyImport_ImportModule("math");

>> m
(PyObject *) 0x7fb41b1b79f8
>> 

>> PyObject_Print(m, stdout, 0), std::cout << std::endl;
<module 'math' from '/usr/lib64/python3.6/lib-dynload/math.cpython-36m-x86_64-linux-gnu.so'>

Get function sin from module math:

// Get function sin from module math 
>> PyObject* py_sin = PyObject_GetAttrString(m, "sin")
(PyObject *) 0x7fb41b1401f8

// Print string representation of function 'sin'
>> PyObject_Print(py_sin, stdout, 0), std::cout << std::endl;
<built-in function sin>
>> 

Call funciton ‘sin’:

>> PyObject* args = Py_BuildValue("(f)", M_PI_2);

>> PyObject_Print(args, stdout, 0), std::cout << std::endl;
(1.5707963267948966,)

>> PyObject* result = PyEval_CallObject(py_sin, args)
(PyObject *) 0x7fb43059f1e0

>> PyObject_Print(result, stdout, 0), std::cout << std::endl;
1.0

Creating a dictionary object or hash table object:

>> PyObject* dc = PyDict_New();

// Heal-allocated object.
>> dc
(PyObject *) 0x7fb41b234558
>> 
 
// Print the string representation of the dictionary object
>> PyObject_Print(dc, stdout, 0), std::cout << std::endl;
{}
>> 

Exit Python and quit REPL:

>> Py_Finalize()
>> .q

Python C-API Objects:

  • Note: The C-function “constructors” return a pointer to PyObject or PyObject* pointer.
C-API ObjectC-function ConstructorPython Object
PyObjectPython ROOT object, all Python objects are an instance of PyObject
PyIntObject
PyLongObject
PyFloatObject
PyStringObject
PyDictObjectPyDict_NewPython dictionary {}
PyTupleObjectPyTuple_NewPython tuple, example: (100, 200, “Location”)
PyListObjectPyList_NewPython list object, example: [100, 2000, ‘x’]
PyVarObject
PyTypeObject
PyByteArrayObject
PyBytesObject
PySetObject
PyUnicodeObject

References:

Creating a native Python module:

Python (CPython) debugging with GDB:

Reverse engineering:

Basic Commands

Show Help

  • Command: .?
Root [3] .?

 Cling (C/C++ interpreter) meta commands usage
 All commands must be preceded by a '.', except
 for the evaluation statement { }
 ==============================================================================
 Syntax: .Command [arg0 arg1 ... argN]

   .L <filename>		- Load the given file or library

   .(x|X) <filename>[args]	- Same as .L and runs a function with
				  signature: ret_type filename(args)

   .> <filename>		- Redirect command to a given file
      '>' or '1>'		- Redirects the stdout stream only
      '2>'			- Redirects the stderr stream only
      '&>' (or '2>&1')		- Redirects both stdout and stderr
      '>>'			- Appends to the given file

   .undo [n]			- Unloads the last 'n' inputs lines

   .U <filename>		- Unloads the given file

   .I [path]			- Shows the include path. If a path is given -
				  adds the path to the include paths

   .O <level>			- Sets the optimization level (0-3)
				  (not yet implemented)

   .class <name>		- Prints out class <name> in a CINT-like style

   .files 			- Prints out some CINT-like file statistics

   .fileEx 			- Prints out some file statistics

   .g 				- Prints out information about global variable
				  'name' - if no name is given, print them all

   .@ 				- Cancels and ignores the multiline input

   .rawInput [0|1]		- Toggle wrapping and printing the
				  execution results of the input

   .dynamicExtensions [0|1]	- Toggles the use of the dynamic scopes and the
				  late binding

   .printDebug [0|1]		- Toggles the printing of input's corresponding
				  state changes

   .storeState <filename>	- Store the interpreter's state to a given file

   .compareState <filename>	- Compare the interpreter's state with the one
				  saved in a given file

   .stats [name]		- Show stats for internal data structures
				  'ast'  abstract syntax tree stats
				  'asttree [filter]'  abstract syntax tree layout
				  'decl' dump ast declarations
				  'undo' show undo stack

   .help			- Shows this information

   .q				- Exit the program


References and Bookmarks

Download:

Documentation:

Root Reflection Documentation:

Questions:

Files: