Search code examples
pythonc++overloadingcython

How to import classes from C++ to Cython that uses function overloading


I have classes nested in classes and inside of a namespace in c++ in the following format:

namespace my_Namespace{
    class MyFirstClass{
       class NestedClass{
          public:
            NestedClass(int arg){};
            NestedClass(double arg{};
            NestedClass(std::string arg){};
       };
    };
};

And from the documentation you import the classes in cython like so:

cdef extern from "myfile.hpp" namespace "my_Namespace":
   cdef cppclass MyFirstClass "my_Namespace::MyFirstClass"
   cdef cppclass NestedClass "my_Namespace::MyFirstClass::NestedClass`

Side note, do we need any imports for the cython script to use cppclass? Im not seeing it as a keyword.

Anyway, since NestedClass has constructor overload how would I implement each in cython? I am trying to do this so that it can be normally imported in python, am I better off just making the whole c++ namespace and functions and putting them in a DLL and just use ctypes to import and use the functions?

And one last question I am seeing the documents create classes from the cython code above like so:

cdef class NestedClass:
    cdef NestedClass* obj_
    def __cinit__(self):
        self.obj_=new NestedClass()
    def __dealloc__(self): del self.obj_

Is this correct and can I just use the NestClass that I got from the C++ file to derive classes like so:

`cdef class NestedClassInt:
   cdef NestedClass* obj_
   def __cinit__(self,arg:int): self.obj_=new NestedClass(arg)
#Now define the define the string one
 def class NestedClassString:
   cdef NestedClass* obj_
   def __cinit__(self,arg:str): self.obj_=new NestedClass(arg)

I have been reading multiple forums and I know that python does not support constructor overloading so I don't see if Cython does or if there is a way to convert from it.


Solution

  • The upshot as I explained in the comments is that you can declare the overloads for your C++ methods and Cython understands them. You can't declare overloads for methods of cdef classes, and therefore need to pick some other way of switching based on type.

    I suggested either using different factory functions (classmethods/staticmethods) or using isinstance in __init__.

    # distutils: language = c++
    
    from libcpp.string cimport string as std_string
    
    cdef extern from * namespace "my_Namespace":
        """
        namespace my_Namespace{
        class MyFirstClass{
          public:
           class NestedClass{
              public:
                NestedClass(int arg){};
                NestedClass(double arg){};
                NestedClass(std::string arg){};
           };
        };
        };
        """
        cdef cppclass MyFirstClass:
            cppclass NestedClass:
                NestedClass(int arg)
                NestedClass(double arg)
                NestedClass(std_string arg)
    
    cdef class NestedClassWrapper:
        cdef MyFirstClass.NestedClass* nc
        def __dealloc__(self):
            del self.nc
    
        @classmethod  # use staticmethod to make this cdef
        def make_from_int(cls, int arg):
            cdef NestedClassWrapper o = cls.__new__(cls)
            o.nc = new MyFirstClass.NestedClass(arg)
            return o
    
        # and the same pattern for the int and string versions
    
    cdef class NestedClassWrapper2:
        cdef MyFirstClass.NestedClass* nc
        def __dealloc__(self):
            del self.nc
    
        def __cinit__(self, arg):
            if isinstance(arg, int):
                # cast to a C int
                self.nc = new MyFirstClass.NestedClass(<int>arg)
            elif isinstance(arg, float):
                self.nc = new MyFirstClass.NestedClass(<double>arg)
            elif isinstance(arg, bytes):
                self.nc = new MyFirstClass.NestedClass(<std_string>arg)
            else:
                raise TypeError
    

    Other variations on the theme might also exist. For example a fused type as an argument to __cinit__, or using try: ... except to test the type conversions rather than isinstance. But most solutions will look something like one of these two approaches.


    cppclass should be recognised providing you name the file .pyx. It may not be highlighted by your editor though.