xunzhang
xunzhang

Reputation: 2936

How to define c++ template function type in Cython?

A.hpp

// A.hpp
#ifndef RECTANGLE_HPP
#define RECTANGLE_HPP
#include <iostream>
#include <vector>
namespace test {

double euclid_dist(const std::vector<double> & a) {
  return 3.1415926;
}

template <class T, 
          double (*dist)(const std::vector<T> &)>
class foo {
 public:
  foo(std::vector<T> _items) {
   items = _items;
  }  
  ~foo() {} 
  void inside() {
    std::cout << "hello" << std::endl;
  }  
  void func() {
    inside();
  }  
  void prt(std::vector<std::vector<T> > a) {
    if(a.size() >= 1) {
      std::cout << euclid_dist(a[0]) << std::endl;
    }  
  }  
 private:
  std::vector<T> items;
}; // class foo
} // namespace test 
#endif

demo.pyx:

# demo.pyx
# distutils: language = c++
from libcpp.vector cimport vector
cdef extern from "A.hpp" namespace "test":
  double euculid_dist(const vector [double] & a) 

#def test_euclid_dist(const vector [double] a):
#    return euclid_dist(a)

cdef extern from "A.hpp" namespace "test":
  cdef cppclass foo [double, euclid_dist]:
      foo (vector[double]) except +
      void func()
      void prt(vector[vector[double]])

cdef class PyFoo:
    cdef foo *thisptr      # hold a C++ instance which we're wrapping
    def __cinit__(self, vec):
        self.thisptr = new foo(vec)
    def __dealloc__(self):
        del self.thisptr
   def func(self):
        self.thisptr.func()
   def prt(self, d):
        self.thisptr.prt(d)

setup.py

from distutils.core import setup, Extension
from Cython.Build import cythonize

setup(ext_modules = cythonize(Extension(
    "demo",                                # the extesion name
    sources=["demo.pyx"], # the Cython source and additional C++ source files
    language="c++", # generate and compile C++ code
)))

When compiling with python setup.py build_ext --inplace, I get error below:

Compiling demo.pyx because it changed.
Cythonizing demo.pyx
running build_ext
building 'demo' extension
x86_64-pc-linux-gnu-g++ -pthread -fPIC -I/usr/include/python2.7 -c         
demo.cpp -o build/temp.linux-x86_64-2.7/rect.o
demo.cpp:551:20: error: ‘euclid_dist’ was not declared in this scope
demo.cpp:551:31: error: template argument 2 is invalid
demo.cpp: In function ‘int __pyx_pf_4rect_5PyFoo___cinit__(__pyx_obj_4rect_PyFoo*, PyObject*)’:
demo.cpp:866:20: error: ‘euclid_dist’ was not declared in this scope
demo.cpp:866:31: error: template argument 2 is invalid
demo.cpp:866:43: error: invalid type in declaration before ‘;’ token
demo.cpp:881:38: error: ‘euclid_dist’ cannot appear in a constant-expression
demo.cpp:881:49: error: template argument 2 is invalid
demo.cpp:881:60: error: cannot convert ‘std::vector<double>’ to ‘int’ in initialization
demo.cpp: In function ‘PyObject* __pyx_pf_4rect_5PyFoo_4func(__pyx_obj_4rect_PyFoo*)’:
demo.cpp:984:26: error: request for member ‘func’ in ‘* __pyx_v_self->__pyx_obj_4rect_PyFoo::thisptr’, which is of non-class type ‘int’
demo.cpp: In function ‘PyObject* __pyx_pf_4rect_5PyFoo_6prt(__pyx_obj_4rect_PyFoo*, PyObject*)’:
demo.cpp:1038:26: error: request for member ‘prt’ in ‘* __pyx_v_self->__pyx_obj_4rect_PyFoo::thisptr’, which is of non-class type ‘int’
error: command 'x86_64-pc-linux-gnu-g++' failed with exit status 1

It seems that in scope it can not find euclid_dist function, but I think it is in global scope..What's wrong with above code? Thanks!

Upvotes: 4

Views: 2105

Answers (1)

Simon Gibbons
Simon Gibbons

Reputation: 7194

Cython doesn't yet have support for non type template parameters, there is a bit of a hacky workaround for this though.

When you create the cython definition of the class you give it the explicit template instantiation so that cython won't need to know about the templated nature of the class.

In your case the definition for foo would be:

cdef extern from "A.hpp" namespace "test":
    cdef cppclass foo "test::foo<double, test::euclid_dist>":
        foo(const vector[double] &) except +
        #... other members

Upvotes: 4

Related Questions