19

I have a C++ class called Foo. If I follow the Cython C++ tutorial I will need to call the Python class differently, PyFoo for example. However I really need to call the Python class Foo as well. How to do that efficiently?

Edit: I'm trying to interface an existing C++ library that was previously interfaced with Boost Python. For different reasons I would like to test Cython instead. Since with Boost:Python Python classes were called with the same name as in C++, I would like to continue with this naming convention. It's not a Python (CPython) requirement to call classes differently, but it seems to be imposed by Cython, at least in the tutorial.

I can of course use a pure python module to define a Foo class that calls PyFoo, but this seems both boring and inefficient.

ascobol
  • 7,554
  • 7
  • 49
  • 70
  • Why not make the C++ class be CppFoo instead? – Karl Knechtel Apr 11 '12 at 23:24
  • 4
    Because then I will need to call the Python class CppFoo as well... – ascobol Apr 12 '12 at 05:29
  • What causes you to think that the name `PyFoo` is special? As far as I can tell, the only real requirement is that the names are different, and the only reason for that is that you are going to refer to the C++ type in the implementation of the Python type. – Karl Knechtel Apr 12 '12 at 05:47
  • I edited my question to be more clear about my goals. – ascobol Apr 12 '12 at 08:20
  • 2
    Why not have a module that works with Cython such that it all works correctly even though you are unhappy with the naming, then make another module that serves as facade, that does little else except something like 'from _CppFoo import *; Foo = PyFoo' – Arafangion Apr 12 '12 at 08:22
  • @Arafangion that's the best solution I've found so far. At first I thought it would slow down calls to the C++ module, but now I realise that it may not be the case. – ascobol Apr 12 '12 at 08:35

2 Answers2

26

There are two ways to handle this.

  1. Declare C++ class with an alternate name; original name has to be specified in double quotes:

    cdef extern from "defs.h" namespace "myns":
        cdef cppclass CMyClass "myns::MyClass":
            ...
    

    Then you can use MyClass for your python class and refer to C++ declaration as CMyClass.

    Note that original name has to include the namespace explicitly (if it is namespaced). Cython template arguments (when present) should go after an alternate name declaration.

  2. Declare your C++ classes in a separate .pxd file, named differently from your .pyx file, then import them with cimport.

    In cpp_defs.pxd:

    cdef extern from "defs.h" namespace "myns":
        cdef cppclass MyClass:
            ...
    

    In py_wrapper.pyx:

    cimport cpp_defs as cpp
    
    cdef class MyClass:
        cpp.MyClass *_obj
    
kynan
  • 13,235
  • 6
  • 79
  • 81
Nikita Nemkin
  • 2,780
  • 22
  • 23
  • 1
    For me, the second option complains about redefining `MyClass` in `py_wrapper.pyx`, even though it seems like `cpp.MyClass` really should be considered a separate thing. The first option works fine for me, though `namespace "myns"` has to be on the previous line as `cdef extern from "defs.h" namespace "myns":` or cython complains about a syntax error. [Using Cython version 0.20.1.] – Mike Mar 06 '14 at 14:33
  • 3
    Is there a way to do this for functions? – Alex Flint Jun 18 '14 at 20:31
  • 1
    What's the problem with functions? Textual override (in double quotes) works for all cdef objects: classes, functions, structs, typedefs, etc. Moving declarations to a separate .pxd (more natural solution to namespacing) also works for everything. – Nikita Nemkin Jun 19 '14 at 05:27
  • 1
    @Mike For the second option, pyd and pyx files must have different names. – aberaud Aug 05 '15 at 15:11
3

Here is a full example demonstrating Nikita's approach:

cdef extern from "floorplan_geometry.h" namespace "flyby::localize":
    cdef cppclass CFloorplan "flyby::localize::Floorplan":
        int xmin()

cdef class Floorplan:
    cdef CFloorplan* obj_
    def __cinit__(self):
        self.obj_ = new CFloorplan()
    def __dealloc__(self):
        del self.obj_
    def xmin(self):
        return self.obj_.xmin()
Alex Flint
  • 6,040
  • 8
  • 41
  • 80