1

I am trying to use a library written in c++ from Python. The library consists of multiple classes that are inherited from a base class, each class has its own .cpp and .h files.

When using Cython to get access to those classes (I only need the child classes, but with methods from the base class), I can only make it work by redefining the base class in the pxd and pyx files for all child classes.

If I try to define a pxd (and pyx) file for the base class, I run into problems that the header for my base class is included multiple times in the .cpp file created by Cython, throwing the error: redefinition of 'class BaseClass'

What is the correct project layout for this kind of setup?

I am working on Ubuntu 16.04LTS, Python 3.5.2, Cython 0.29.7

As the MWE already involves quite a few files, I only show here a few snippets, but the full code is on GitHub: https://github.com/Heerpa/test_cython_cpp/commits/master

In the MWE, I have a base class Mammal, and two child classes Cat and Dog. The child classes use the method run() from the Mammal class but define their own eat() methods.

The layout that would make sense to me is do have files for the base class:

# c_mammal.pxd

def extern from 'CMammal.h':
    cdef cppclass CMammal:
        void run()

and a definition for the child classes, e.g. Cat:

# c_cat.pxd
from c_mammal cimport CMammal


cdef extern from 'CCat.h':
    # cdef cppclass CMammal:
    #     void run()
    cdef cppclass CCat(CMammal):
        void eat()

now, as CCat.h includes CMammal.h in order to inherit from it, I get the redefinition error

building 'animals.dog' extension
x86_64-linux-gnu-gcc -pthread -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC -Isrc/animalcpplibrary/ -I/usr/include/python3.5m -c src/dog.cpp -o build/temp.linux-x86_64-3.5/src/dog.o
cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++
In file included from src/animalcpplibrary/CDog.h:2:0,
                 from src/dog.cpp:623:
src/animalcpplibrary/CMammal.h:3:7: error: redefinition of ‘class CMammal’
 class CMammal {
       ^
In file included from src/dog.cpp:622:0:
src/animalcpplibrary/CMammal.h:3:7: error: previous definition of ‘class CMammal’
 class CMammal {
       ^
error: command 'x86_64-linux-gnu-gcc' failed with exit status 1

If instead I use the fact that CMammal is included in CCat, i can do:

# c_cat.pxd
cdef extern from 'CCat.h':
    cdef cppclass CMammal:
        void run()
    cdef cppclass CCat(CMammal):
        void eat()

and also define functionality of the Mammal in cat.pyx:

# cat.pyx

# distutils: sources = [src/animalcpplibrary/CCat.cpp, src/animalcpplibrary/CMammal.cpp]
# distutils: language = c++
# distutils: include_dirs = src/animalcpplibrary/
# cython: language_level=3

cimport c_cat

cdef class PyMammal:
    cdef c_cat.CMammal* thisptr

    def run(self):
        if self.thisptr:
            self.thisptr.run()


cdef class PyCat(PyMammal):
    def __cinit__(self):
        if self.thisptr:
            del self.thisptr
        print('cinit Cat: allocating instance.')
        self.thisptr = new c_cat.CCat()

    def __dealloc__(self):
        if self.thisptr:
            print('dealloc Cat: deallocating instance')
            del self.thisptr

    def eat(self):
        if self.thisptr:
            (<c_cat.CCat*>self.thisptr).eat()

and, the point is, I have to write the exact same Mammal code also in the dog cython files. This works but can't be the intended solution.

Heinrich
  • 113
  • 1
  • 12
  • 4
    Does your mamal.h file have include guards? It should. – Martin Bonner supports Monica May 03 '19 at 16:12
  • I've looked at the git repo. No it doesn't. (Also, you shouldn't include iostream in mammal.h - you don't need any of the definition from iostream in order to define class mammal. – Martin Bonner supports Monica May 03 '19 at 16:15
  • Awesome, thanks! Maybe I should have added that my C++ days were short and long ago. The include guards make it work (https://en.wikipedia.org/wiki/Include_guard). I do use std::cout in mammal.cpp, and I thought to remember that all includes for a .cpp go into its header!? Is there something more I can learn? – Heinrich May 03 '19 at 21:18

0 Answers0