CPP / C++ Review

Table of Contents

1 C++ CPP CERN - Root Cling Interpreter and tools

1.1 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 …"

1.2 Installation and configuration

1.2.1 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' |
   ------------------------------------------------------------

1.2.2 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

1.2.3 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

1.3 Command Sumamry

REPL Command Description
.? Show help
.q Exit ROOT shell
.L file.cpp Load file.cpp, so it loads all the file's classes and functions
.x script.cxx Load and execute ROOT script or C++ ordinary source code. The entry point is void script()
.include Show 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 TFile Show 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]

1.4 Playing with Root REPL

1.4.1 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] 

1.4.2 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] 

1.4.3 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] 

1.4.4 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

1.4.5 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] 

1.4.6 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] 

1.4.7 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] 


1.4.8 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

1.4.9 Playing with classes

  1. 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] 
    
    
  2. 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;
    }
    

1.4.10 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] 

1.4.11 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

1.4.12 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
 ... ...  ... ...  ... ...  ... ...  ... ...  ... ... 

1.4.13 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] 

1.4.14 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);

1.4.15 Testing Unix System-Calls and APIs

  1. 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] 
    
    
  2. 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] 
    
  3. 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] 
    
  4. 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] 
    
    

1.4.16 Run a script with shared library

1.5 C++ Script Examples

1.5.1 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))

1.5.2 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)

1.6 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:

C-API Object C-function Constructor Python Object
PyObject   Python ROOT object, all Python objects are an instance of PyObject
     
PyIntObject    
PyLongObject    
PyFloatObject    
PyStringObject    
     
PyDictObject PyDict_New Python dictionary {}
PyTupleObject PyTuple_New Python tuple, example: (100, 200, "Location")
PyListObject PyList_New Python list object, example: [100, 2000, 'x']
PyVarObject    
PyTypeObject    
PyByteArrayObject    
PyBytesObject    
PySetObject    
PyUnicodeObject    
     
  • Note: The C-function "constructors" return a pointer to PyObject or PyObject* pointer.

References:

Creating a native Python module:

Python (CPython) debugging with GDB:

Reverse engineering:

1.7 Basic Commands

1.7.1 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


Created: 2021-06-04 Fri 15:10

Validate