Templates with ROOT6

Hi, I tried searching on the forum and only found advice relating to ROOT5 specifically. Hopefully with ROOT6 things are easier than before.

I have in a file MySpline.C a recursive function template like this:

[code]template
double Poly(const double * const x, const double * const c)
{ return c[0] + x[0]*Poly(x,c+1); }

// This specialization ensures that the recursion terminates.
template <>
double Poly<1>(const double * const, const double * const c)
{ return c[0]; }[/code]

In cling, I can load it with .L MySpline.C+, and I can check that it works by feeding a double *, double *.

Now I am trying to use it from a PyROOT script. My first attempt is immediately foiled:

In [3]: ROOT.gROOT.ProcessLine('.L MySpline.C+') Info in <TMacOSXSystem::ACLiC>: creating shared library /Users/jfcaron/Projects/Proto2BeamTest2/code_test/./MySpline_C.so ld: can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB) file '/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-dynload/time.so' for architecture x86_64 clang: error: linker command failed with exit code 1 (use -v to see invocation) Error in <ACLiC>: Compilation failed!

I remember reading some terse advice saying “cling compiles code” where I can just #include files instead of needing ACliC, so I try instead, but then the syntax isn’t clear. From the error message for Poly(‘3’)(x,c), it looks like Poly for N 1-6 are instantiated automatically, but I don’t know how to call them. Poly(x,c) gives the wrong answer.

[code]In [2]: ROOT.gROOT.ProcessLine(’#include “MySpline.C”’)
Out[2]: 0L
In [3]: ROOT.Poly
Out[3]: <ROOT.MethodProxy at 0x10a317ed0>
In [6]: import array
In [7]: x = array.array(‘d’,[5])
In [8]: c = array.array(‘d’,[1,2,3])
In [9]: ROOT.Poly(‘3’)(x,c)

TypeError Traceback (most recent call last)
in ()
----> 1 ROOT.Poly(‘3’)(x,c)

TypeError: none of the 6 overloaded methods succeeded. Full details:
double ::Poly<1>(const doubleconst x, const doubleconst c) =>
takes at least 2 arguments (1 given)
double ::Poly<3>(const doubleconst x, const doubleconst c) =>
takes at least 2 arguments (1 given)
double ::Poly<2>(const doubleconst x, const doubleconst c) =>
takes at least 2 arguments (1 given)
double ::Poly<6>(const doubleconst x, const doubleconst c) =>
takes at least 2 arguments (1 given)
double ::Poly<5>(const doubleconst x, const doubleconst c) =>
takes at least 2 arguments (1 given)
double ::Poly<4>(const doubleconst x, const doubleconst c) =>
takes at least 2 arguments (1 given)
In [10]: ROOT.Poly(x,c)
Out[10]: 1.0
[/code]

The previous advice said I should load dictionaries, but that this shouldn’t be needed for ROOT6:
[url]Copy std::vector from one TTree to another
[url]C++ templates in PyRoot

Jean-François

It looks like the instantiations Poly for N = 1 through 6 are because of extra code I have in MySpline.C that runs some tests. Unfortunately I can’t call them either (or I don’t know the syntax).

I tried a few workarounds to explicitly specialize the template, but nothing worked except an undesireable one. Here’s what I tried:

  1. Add the following line to MySpline.C:

The result:

[code]In [2]: ROOT.gROOT.ProcessLine(’#include “MySpline.C”’)
Out[2]: 0L
In [3]: ROOT.Poly3
/Users/jfcaron/Software/custom_root/root6/root/lib/ROOT.py:488: RuntimeWarning: creating converter for unknown type "double(const)(const double,const double*)"
attr = _root.LookupRootEntity( name, PyConfig.ExposeCppMacros )
Out[3]: <ROOT.PropertyProxy at 0x11789d978>

Create arrays x and c as before:

In [7]: ROOT.Poly3(x,c)

TypeError Traceback (most recent call last)
in ()
----> 1 ROOT.Poly3(x,c)

TypeError: ‘ROOT.PropertyProxy’ object is not callable
[/code]

  1. Add the function:

double Poly3(const double * const x, const double * const c) { return Poly<3>(x,c); }
This one actually works, but it’s a lot of typing and makes me want to write a pre-processor macro for lots of N, which is probably a bad idea.

Jean-François

Jean-François,

you’re suffering from a bit of history here … global templated functions rely on the overload selection through their arguments only. In your case, all the argument types are the same. Put the Poly() function in a class:[code]class MySpline {
public:
template
static double Poly(const double * const x, const double * const c)
{ return c[0] + x[0]*Poly(x,c+1); }
};

// This specialization ensures that the recursion terminates.
template <>
double MySpline::Poly<1>(const double * const, const double * const c)
{ return c[0]; }[/code]
use it as “ROOT.MySpline.Poly(3)(x, c)” (or “ROOT.MySpline.Poly(‘3’)(x, c)” if you insist) and you should be in business.

Note that this is python, so you can do:ROOT.Poly = ROOT.MySpline.Polystraight after the ProcessLine() and then use ‘ROOT.Poly’ the way you want to.

(This of course assumes that you can modify the C++ code. If not, and a small helper class that forwards the calls isn’t an option, let me know. This history needs cleaning up at some point anyhow.)

Cheers,
Wim

I modified my code to put the Poly functions in a class MySpline. I also added namespace for the sake of the C+±side of my code. Also it turns out I need this to work for both ROOT5 and ROOT6 (and with PyROOT), I don’t know if that’s even possible anymore…

Unfortunately my example was too simplified. The full code is attached to this post.

Here’s what I see with ROOT6:

*  ROOT v6.02/00-rc1  *
root [0] .L MySpline.C+
Info in <TMacOSXSystem::ACLiC>: creating shared library /Users/jfcaron/Projects/Proto2BeamTest2/code_test/./MySpline_C.so
root [1] double x[] = {5}
(double [1]) { 5.000000e+00 }
root [2] double c[] = {1,2,3,4,5}
(double [5]) { 1.000000e+00, 2.000000e+00, 3.000000e+00, 4.000000e+00, 5.000000e+00 }
root [3] MySpline::MySpline::Poly<5>(x,c)
(double) 3.711000e+03
root [4] MySpline::MySpline::PolyDeriv<5,2>(x,c)
(double) 1.626000e+03
root [5] double k[] = {3}
(double [1]) { 3.000000e+00 }
root [6] MySpline::PieceWise<1,3> pw(k)
(MySpline::PieceWise<1, 3> &) @0x1145430a0
root [7] pw(x,c)
(double) 1.220000e+02

and I can use the test1, test2, test3, test4 functions from MySpline.

In PyROOT, I see this:

In [1]: import ROOT In [2]: ROOT.gROOT.ProcessLine(".L MySpline.C+") Info in <TMacOSXSystem::ACLiC>: creating shared library /Users/jfcaron/Projects/Proto2BeamTest2/code_test/./MySpline_C.so ld: can't link with bundle (MH_BUNDLE) only dylibs (MH_DYLIB) file '/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-dynload/cStringIO.so' for architecture x86_64 clang: error: linker command failed with exit code 1 (use -v to see invocation) Error in <ACLiC>: Compilation failed! Out[2]: 0L

Actually that’s a separate problem where somehow ACLiC doesn’t work in PyROOT, but if I .L MySpline.C+ from regular ROOT before, then I can load and use the .so without problems:

In [1]: import ROOT,array
In [2]: ROOT.gROOT.ProcessLine(".L MySpline.C+")
Out[2]: 0L
In [3]: x = array.array('d',[5])
In [4]: c = array.array('d',[1,2,3,4,5])
In [5]: k = array.array('d',[3])
In [6]: ROOT.MySpline.MySpline.Poly(5)(x,c)
Out[6]: 3711.0
In [7]: ROOT.MySpline.MySpline.PolyDeriv(5,2)(x,c)
Out[7]: 1626.0
In [8]: pw = ROOT.MySpline.PieceWise(1,3)(k)
In [9]: pw(x,c)
Out[9]: 122.0

Now if I switch to ROOT5, the nothing works in CINT:

*  ROOT v5.34/18  *

root [0] .L MySpline.C++
Info in <TMacOSXSystem::ACLiC>: creating shared library /Users/jfcaron/Projects/Proto2BeamTest2/code_test/./MySpline_C.so
root [1] double x[] = {5}
root [2] double c[] = {1,2,3,4,5}
root [3] double k[] = {3}
root [4] MySpline::MySpline::Poly<5>(x,c)
Error: > Illegal operator for pointer 3 (tmpfile):1:
(const int)0
*** Interpreter error recovered ***
root [5] MySpline::MySpline::PolyDeriv<5,2>(x,c)
Error: Symbol PolyDeriv is not defined in current scope  (tmpfile):1:
Warning: Illegal numerical expression 5,2 (tmpfile):1:
Error: > Illegal operator for pointer 3 (tmpfile):1:
(const int)0
*** Interpreter error recovered ***
root [6] MySpline::PieceWise<1,3> pw;
Error: Symbol PieceWise is not defined in current scope  (tmpfile):1:
Warning: Illegal numerical expression 1,3 (tmpfile):1:
Error: Symbol pw is not defined in current scope  (tmpfile):1:
*** Interpreter error recovered ***

and now PyROOT with ROOT5:

In [1]: import ROOT,array
In [2]: ROOT.gROOT.GetVersion()
Out[2]: '5.34/18'
In [3]: ROOT.gROOT.ProcessLine(".L MySpline.C+")
Info in <TMacOSXSystem::ACLiC>: creating shared library /Users/jfcaron/Projects/Proto2BeamTest2/code_test/./MySpline_C.so
Out[3]: 0L
In [4]: x = array.array('d',[5])
In [5]: c = array.array('d',[1,2,3,4,5])
In [6]: k = array.array('d',[3])
In [7]: ROOT.MySpline.MySpline.Poly(5)(x,c)
 *** Break *** segmentation violation
 Generating stack trace...
 0x0000000105144303 in PyObject_Call (in Python) + 99
 0x00000001051e21ae in PyEval_EvalFrameEx (in Python) + 16030
 0x00000001051de09d in PyEval_EvalCodeEx (in Python) + 1725
 0x00000001051e2e70 in PyEval_EvalFrameEx (in Python) + 19296
 0x00000001051de09d in PyEval_EvalCodeEx (in Python) + 1725
 0x00000001051e5c29 in fast_function (in Python) + 297
 0x00000001051e14e5 in PyEval_EvalFrameEx (in Python) + 12757
 0x00000001051de09d in PyEval_EvalCodeEx (in Python) + 1725
 0x00000001051e5c29 in fast_function (in Python) + 297
 0x00000001051e14e5 in PyEval_EvalFrameEx (in Python) + 12757
 0x00000001051de09d in PyEval_EvalCodeEx (in Python) + 1725
 0x00000001051e5c29 in fast_function (in Python) + 297
 0x00000001051e14e5 in PyEval_EvalFrameEx (in Python) + 12757
 0x00000001051de09d in PyEval_EvalCodeEx (in Python) + 1725
 0x00000001051e5c29 in fast_function (in Python) + 297
 0x00000001051e14e5 in PyEval_EvalFrameEx (in Python) + 12757
 0x00000001051de09d in PyEval_EvalCodeEx (in Python) + 1725
 0x00000001051e5c29 in fast_function (in Python) + 297
 0x00000001051e14e5 in PyEval_EvalFrameEx (in Python) + 12757
 0x00000001051de09d in PyEval_EvalCodeEx (in Python) + 1725
 0x00000001051e5c29 in fast_function (in Python) + 297
 0x00000001051e14e5 in PyEval_EvalFrameEx (in Python) + 12757
 0x00000001051de09d in PyEval_EvalCodeEx (in Python) + 1725
 0x0000000105169dfc in function_call (in Python) + 364
 0x0000000105144303 in PyObject_Call (in Python) + 99
 0x00000001051e180d in PyEval_EvalFrameEx (in Python) + 13565
 0x00000001051de09d in PyEval_EvalCodeEx (in Python) + 1725
 0x00000001051e5c29 in fast_function (in Python) + 297
 0x00000001051e14e5 in PyEval_EvalFrameEx (in Python) + 12757
 0x00000001051de09d in PyEval_EvalCodeEx (in Python) + 1725
 0x00000001051dd9d6 in PyEval_EvalCode (in Python) + 54
 0x0000000105207544 in PyRun_FileExFlags (in Python) + 164
 0x00000001052070c1 in PyRun_SimpleFileExFlags (in Python) + 769
 0x000000010521cd1e in Py_Main (in Python) + 3070
 0x00007fff90dd75fd in start (in libdyld.dylib) + 1

I tried removing the namespace for ROOT5, but it doesn’t change anything. In ROOT5 I can run the test1()…test4() functions and they work.

So, is it possible to make my few templated classes work? Why does MySpline::MySpline::Poly work but MySpline::PieceWise? I guess it’s because PieceWise is itself templated, while Poly is a templated method of a non-templated class?

Jean-François
MySpline.C (7.98 KB)

Hi,

that ACLiC error comes about b/c ACLiC links the newly generated library with all loaded libraries. I’m surprised that cStringIO is loaded, though. It’s a python extension module and should be there unless imported. Likely that only affects iPython on Mac (the former b/c bare CPython will not load the module; the latter b/c only Mac has this concept of bundles). May even be only your system, depending on how python was build …

From regular root it works b/c cStringIO isn’t loaded, so ACLiC does not list it for the libraries to link with.

As for templates in ROOT5, i.e. CINT, there it is necessary to fully instantiate all templates you intend to use, and to generate dictionaries for them. CINT does not natively know about templates and definitely can not instantiate them.

Cheers,
Wim

Right, sorry I forgot I already knew the answer for ROOT5 and templates when I wrote that post. Actually I don’t know the answer, but I know the problem. I’ve asked on the main ROOT forum for help getting my templated members and class to work in CINT: [url]ACLiC Pragmas to Access Templated Class & Members in CINT

Also I don’t actually need this to work with both ROOT5 and ROOT6, but I have a problem related to that:

So a sufficient but not-necessary condition is to get my templates to work in ROOT5. Another would be to get the other C++ code to work in ROOT6.

Jean-François