1

I'm trying to wrap an entire c++ template library with cython in order to let it available for python users. As I already mentioned, the library is template on both class and method of the class and as consequence, it is not compiled but "header-only". Since the library is somewhat big, I decided to create a file as a starting point and trying to wrap it in cython and let it available as a python function. The file, let call it easy.hpp, reflects the way the library is written

#include <iterator>
#include <utility>
#include <vector>
#include <iostream>

namespace exNamespace {

class exClass
{
public:
   template <typename T1, typename T2> void exMethod(T1 a, T2 b) const;
};

// Impl
template <typename T1, typename T2> void exClass::exMethod(T1 a, T2 b) const
{

    for(auto i=0; i< a.size(); i++){
        std::cout << a[i] << "-" << b[i] << "\n";
    }

    }
}

Following "Cython-A guide for Python programmers by Kurt Smith", I built two file: tWrap.pxd and tWrap.pyx:

//tWrap.pxd
cdef extern from "easy.hpp" namespace "exNamespace":
    cdef cppclass exClass:
        void exMethod[T1,T2](T1 a, T2, b) except +

//tWrap.pyx
from libcpp.vector cimport vector
cimport tWrap


ctypedef vector[double].iterator vvit

cdef _process(v1, v2):
    cdef vector[double] vv1
    cdef vector[double] vv2
    assert len(v1) == len(v2)        
    for elt1 in v1:
        vv1.push_back(<double>elt1)
    for elt2 in v2:
        vv2.push_back(<double>elt2)
    exClass.exMethod(vv1.begin(),vv2.begin())

def process(v1,v2):
    _process(v1,v2)

The setup.py is written as follow:

import os

from setuptools import setup, Extension
from setuptools.dist import Distribution
from Cython.Distutils import build_ext

Distribution(dict(setup_requires='Cython'))

ext_modules = [
     Extension("test", ["tWrap.pyx"],
              language="c++"
            )
]

setup(
    include_package_data=True,
    package_data={'': ['*.pyx', '*.pxd', '*.h', '*.c']},
    cmdclass = {'build_ext': build_ext},
    ext_modules=ext_modules
) 

In compiling stage, I get stuck with these errors:

Error compiling Cython file:
------------------------------------------------------------
...
    assert len(v1) == len(v2)        
    for elt1 in v1:
        vv1.push_back(<double>elt1)
    for elt2 in v2:
        vv2.push_back(<double>elt2)
    exClass.exMethod(vv1.begin(),vv2.begin())
   ^
------------------------------------------------------------

tWrap.pyx:26:4: undeclared name not builtin: exClass

Error compiling Cython file:
------------------------------------------------------------
...
    assert len(v1) == len(v2)        
    for elt1 in v1:
        vv1.push_back(<double>elt1)
    for elt2 in v2:
        vv2.push_back(<double>elt2)
    exClass.exMethod(vv1.begin(),vv2.begin())
                             ^
------------------------------------------------------------

tWrap.pyx:26:30: Cannot convert 'iterator' to Python object

Error compiling Cython file:
------------------------------------------------------------
...
    assert len(v1) == len(v2)        
    for elt1 in v1:
        vv1.push_back(<double>elt1)
    for elt2 in v2:
        vv2.push_back(<double>elt2)
    exClass.exMethod(vv1.begin(),vv2.begin())
                                         ^
------------------------------------------------------------

tWrap.pyx:26:42: Cannot convert 'iterator' to Python object

I have to admit to not fully understand how templates are managed in cython, since in the book the author always start from template function coming form std library.

I want to achieve something like:

import test
test.process([1,3,5],[2,4,6])

1-2
3-4
5-6

or by using list(containers) with printable (<< operator) types.

Update

Trying to get an instace of the class:

//tWrap.pyx
...
cdef class cClass:
    cdef tWrap.exClass *_thisptr
    def __cinit__(self):
        self._thisptr = new tWrap.exClass()
    def __dealloc__(self):
        if self._thisptr != NULL:
            del self._thisptr
...

    cdef _process(self,v1, v2):
        cdef vector[double] vv1
        cdef vector[double] vv2
        assert len(v1) == len(v2)        
        for elt1 in v1:
            vv1.push_back(<double>elt1)
        for elt2 in v2:
            vv2.push_back(<double>elt2)
        self._thisptr.exMethod(vv1.begin(),vv2.begin())
...

But fail in compilation with this error:

        self._thisptr.exMethod(vv1.begin(),vv2.begin())
                             ^
------------------------------------------------------------

tWrap.pyx:26:30: Call with wrong number of arguments (expected 3, got 2)
Luca Jungla
  • 150
  • 8
  • 1
    `exClass.exMethod(vv1.begin(),vv2.begin())` would not compile also in c++ because it isn't a static method. Cython's error message could be better, but it just assumes `exClass` is a python object. – ead Mar 06 '20 at 13:46
  • @ead Should I create an istance of the class? Could you be more explicit with the static method? It means `exClass.exMethod()` is not allowed? – Luca Jungla Mar 07 '20 at 14:19
  • Either it is supposed to be a static method, see https://stackoverflow.com/q/31119814/5769463 or if not, you need an instance of the class. – ead Mar 07 '20 at 15:39
  • @ead Well...How will you fix this? – Luca Jungla Mar 07 '20 at 16:47
  • you have made a mistake on declaring the class in `tWrap.pxd`, it should be ```cdef cppclass exClass: void exMethod[T1,T2](T1 a, T2 b) except +``` – bactone May 08 '23 at 09:41

0 Answers0