Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python, using ctypes to create C++ class wrapper

Tags:

python

c

dll

ctypes

I'm well aware that there is no standard ABI for c++, so this is what I did:

//trialDLL.h
#ifndef TRIALDLL_H_
#define TRIALDLL_H_

class MyMathFuncs
{
private:
    double offset;

public:
    MyMathFuncs(double offset);

    ~MyMathFuncs();

    double Add(double a, double b);

    double Multiply(double a, double b);

    double getOffset();
};

#ifdef __cplusplus
extern "C"{
#endif

#ifdef TRIALDLL_EXPORT
#define TRIALDLL_API __declspec(dllexport)
#else
#define TRIALDLL_API __declspec(dllimport)
#endif

    TRIALDLL_API MyMathFuncs* __stdcall new_MyMathFuncs(double offset);

    TRIALDLL_API void __stdcall del_MyMathFuncs(MyMathFuncs *myMath);

    TRIALDLL_API double __stdcall MyAdd(MyMathFuncs* myMath, double a, double b);


#ifdef __cplusplus
}
#endif

#endif

And the definition .cpp: (Other class functions' definitions are omitted)

//trialDLL.cpp
#include "trialDLL.h"

MyMathFuncs* __stdcall new_MyMathFuncs(double offset)
{
return new MyMathFuncs(offset);
}


void __stdcall del_MyMathFuncs(MyMathFuncs *myMath)
{
    myMath->~MyMathFuncs();
}


double __stdcall MyAdd(MyMathFuncs *myMath, double a, double b)
{
return myMath->Add(a, b);
}

// class functions
double MyMathFuncs::Add(double a, double b)
{
return a+b+ this->offset;
}

And I build this into a dll and named it trialDLL3.dll. Then in python, I wrote a module as:

#trialDLL3.py
import ctypes
from ctypes import WinDLL

class MyMath(object):
    def __init__(self, offset):
        self.FunMath = WinDLL('trialDLL3.dll')
        self.FunMath.new_MyMathFuncs.argtypes = [ctypes.c_double]
        self.FunMath.new_MyMathFuncs.restype = ctypes.c_void_p

        self.FunMath.MyAdd.argtypes = [ctypes.c_void_p, \
                                       ctypes.c_double, ctypes.c_double]
        self.FunMath.MyAdd.restype = ctypes.c_double

        self.obj = self.FunMath.new_MyMathFuncs(offset)

    def FunAdd(self, a, b):
        self.FunMath.MyAdd(self.obj, a, b)

    def delete(): 
        self.FunMath.del_MyMathFuncs()

After all these, strange things happened. In the IDLE python shell, I did:

theMath = MyMath(3.3)        #create the instance
theMath.FunAdd(3.3, 3.3)     #call the function

The second line returned None instead of 9.9. Then I tried another way round, putting this line in the shell:

theMath.FunMath.MyAdd(theMath.obj, 3.3 ,3.3)

And this line returns me an unsurprising 9.9, but surprising when compared to the last result None. Shouldn't these two lines identical? And I decided to run all those lines explicitly in python shell and see what can go wrong, writing: (excluding the imports)

loadedDLL = WinDLL('trialDLL3.dll')
loadedDLL.new_MyMathFuncs.argtypes = [ctypes.c_double]
loadedDLL.new_MyMathFuncs.restype = ctypes.c_void_p
loadedDLL.MyAdd.argtypes = [ctypes.c_void_p, \
                                    ctypes.c_double, ctypes.c_double]
loadedDLL.MyAdd.restype = ctypes.c_double
obj = loadedDLL.new_MyMathFuncs(3.3)
FunMath.MyAdd(obj, 3.3, 3.3)

All these lines finally returned 9.9. Aren't these lines identical to the two lines if the trialDLL3.py module is imported?

theMath = MyMath(3.3)        #create the instance
theMath.FunAdd(3.3, 3.3)     #call the function

If they are the same deal, why the two line class version returns None and the explicit way return expected 9.9? Thanks in advance!

like image 913
springRoll Avatar asked Apr 28 '13 21:04

springRoll


1 Answers

Everything works fine... but you forgot to pass along the return value of the C function in the method MyMath.FunAdd!

def FunAdd(self, a, b):
    return self.FunMath.MyAdd(self.obj, a, b)
    ^^^^^^
like image 57
Armin Rigo Avatar answered Oct 13 '22 00:10

Armin Rigo