'''
This is a ctypes python example.
It shows how to communicate with a c++ compiled dll.
The reason why we do this is mainly performance or to make python communicate with other libraries.
In this example we will create a 2d array (matrix) in python and send it to c++.
In c++ we will smooth it and print it and send it back as example.
'''
'''
Also i descided to keep the example flat in just one function. and not put it into a python class so its easier to understand.
'''
import ctypes
import time
## Define the size of the matrix we will make.
Columns = 4
Rows = 4
def PythonToCPP():
#######################################
## Initialisation
#######################################
## Define our libraray path
libPath = r"..\c_pp_python\dll\c_pp_python.dll"
## I have seen some resources where people prefer to do C types in caps.
## I kinda like it too so Im doing the same here. Its just a personal preference.
DOUBLE = ctypes.c_double
PDOUBLE = ctypes.POINTER(DOUBLE)
PPDOUBLE = ctypes.POINTER(PDOUBLE)
INT = ctypes.c_int
## Makes a list of n doubles
## This variable can be send to a c function that takes " double * "
DOUBLE_LIST = DOUBLE*Columns
## [1.0,1.0,1.0, ... ]
## Makes a array of n double pointers
## So every pointer points to a list of doubles.
## This variable can be send to a c function that takes " double ** " (note the double pointer star)
PDOUBLE_Array = PDOUBLE * Rows
## [[1.0,1.0,1.0, ... ],[1.0,1.0,1.0, ... ],...]
## Now we initialize our array with doubles
doublePointer = PDOUBLE_Array()
## Initialize the array with some values
for i in xrange(Rows):
doublePointer[i] = DOUBLE_LIST()
for ii in xrange(Columns):
doublePointer[i][ii] = i+ii
#############################
## Load our compiled library
#############################
lib = ctypes.cdll.LoadLibrary( libPath )
## Initialise our c++ class and store it in a variable.
## This object we pass in every time we call a function. You can kind of see it as the " self " in a python class. def __init__(self)
obj = lib.PythonCallsNew();
## Lets check if our object is still valid.
print "1) Are we good to go? ", "Yes " if lib.is_valid(obj) == 1 else " No "
print "Press enter to continue!"
raw_input()
## Lets create the array in c++. We need to also inform c++ how big our array is when we created it.
## Note that we are putting the return value into a result. This somehow prevents python from garbage collecting.
## now the garbage collection should only happen when a result goes out of scope but this seems to do the trick.
result = lib.create_array(obj,doublePointer,Rows,Columns)
print "2) We created an array in c++"
## Lets print it from c++.
result = lib.print_array(obj)
## We smooth our array by 50%
result = lib.smooth_array(obj, 50)
print "3) We smoothed the array by 50 % "
## Now lets print it from the python side.
print "\nPrinting from Python"
for i in xrange(Rows):
for ii in xrange(Columns):
print "\t",doublePointer[i][ii] ,
print
print "\tEnd printing from python."
print "\n4)Isnt this nice even though we changed it in c++ we still have access to the values from python!!"
print "Press enter to continue!"
raw_input()
## But what if we want to get a copy of the data so it doesnt change...
## We have a function that gets us a copy of the data from c++ (of course we can do this in python too but for example sake :) )
## However if we just run the function from python we will get garbage data. (pointer value)
## i.e 2821472 instead of a python object like<__main__.LP_LP_c_double object at 0x0000000002580AC8>
## So we need to tell python ctypes what kind of data we are expecting
lib.get_array_copy.restype = PPDOUBLE
print "5)We made a copy of the array "
arrayCopy = lib.get_array_copy(obj)
## We smooth our array by 50%
result = lib.smooth_array(obj, 5)
print "6)We smoothed the array by 50 % again "
print "\nPrinting copy from Python"
for i in xrange(Rows):
for ii in xrange(Columns):
print "\t",arrayCopy[i][ii] ,
print
print "\tEnd printing copy from python."
print "7) As you can see the copied array did not change after the smooth"
print "Press enter to continue!"
raw_input()
## Lets print it from c++ again
result = lib.print_array(obj)
print "8) But our c++ array did of course :) \n"
print "Press enter to continue!"
raw_input()
#######################################
## Speed testing
#######################################
print "Now i also did some speed comparissons so you get an idea of the performance increase."
print "Please do note that there are a LOT of optimisations that can be done on the Python and C++ side."
print "Lets reset the values in the arrays and lets create a python equivalent with a list."
## Initialize the array with some values
speedTestPythonArray = []
for i in xrange(Rows):
#doublePointer[i] = DOUBLE_LIST()
speedTestPythonArray.append( [0]*Columns)
for ii in xrange(Columns):
doublePointer[i][ii] = i+ii
speedTestPythonArray[i][ii] = i+ii
print "Press enter to start the performance test!"
raw_input()
cppStartTime = time.time()
print "Calling the c++ smooth function 100.000 times"
for x in xrange( 100000 ):
result = lib.smooth_array(obj, 1)
cppEndTime = time.time() - cppStartTime
print "\nPrinting the c++ array"
for i in xrange(Rows):
for ii in xrange(Columns):
print "\t",doublePointer[i][ii] ,
print
pythonStartTime = time.time()
print "Calling the python smooth function 100.000 times"
for x in xrange( 100000):
speedTestPythonArray = pythonSmooth(speedTestPythonArray, 1)
pythonEndTime = time.time() - pythonStartTime
print "\nPrinting the Python list"
for i in xrange(Rows):
for ii in xrange(Columns):
print "\t",speedTestPythonArray[i][ii] ,
print
print "Duration c++ ",cppEndTime
print "Duration python",pythonEndTime
print """So as you can see there is a clear speed improvement
C++ will most likely still be below 1 second (depending on your machine).
Ony my machine in release c++ is about 15 times faster than python.
Duration c++ 0.0380001068115
Duration python 0.676999807358
Note that running it in debug is slower than in release.
Build the code in release and press Ctrl-F5
The higher of numbers of rows anc colums the bigger the difference becomes. :)
Try setting it to 20 or 40
Thats all folks. Hope it helps :)
"""
## The python smooth function.
def pythonSmooth(arrList, percentage):
total = 0
mean = 0
perc = percentage *0.01;
for y in xrange(Rows):
total = 0;
for x in xrange(Columns):
total += arrList[y][x];
mean = total / float(Columns)
for x in xrange(Columns):
if arrList[y][x] > mean:
arrList[y][x] = arrList[y][x]- ((arrList[y][x]-mean)*perc);
elif arrList[y][x] < mean:
arrList[y][x] = arrList[y][x] + ((mean - arrList[y][x])*perc);
return arrList
PythonToCPP()
print "Press enter to exit"
raw_input()