Calling C++ functions

Hi,
I need to call a C++ function (or a class, if there is such an option), from Panda’s Python code. A function will be part of a DLL, I can also define it in the lib… whatever necessary.

Could you please point me to a proper example? As simple as possible, like call a function, pass it parameters, get the return value, and/or values of parameters, passed by reference?

Thatns in advance,
Alex

If you’re looking for a way to extend python using C++ like panda does, I’d recomend using Boost.Python, it gives you a much easier interface to work with then the raw python bindings. You can find some examples here:

codesampler.com/python.htm

scroll down to the bottom of the page.

If Boost is an overkill, you should look through the documentation on python.org:

docs.python.org/ext/intro.html

Hello

There are several ways to call a C/C++ function from Python. First, you can access C/C++ functions directly from Python, using the ctypes module. In Python 2.5 it is already included, for Python 2.4 you have to download it from sourceforge:
http://sourceforge.net/project/showfiles.php?group_id=71702
Here is a short example, for accessing some of the the functions contained in msvcrt dll (should be on every windows system, and in the search path):

from ctypes import *

libc = cdll.msvcrt

print libc
print libc.time

print libc.time( None )
libc.printf( 'Hello, World!\n' )
libc.printf( '%d bottles of beer\n', 42 )

As simple as possible, just calling functions and passing arguments. But you should still read the tutorial for this module, or you will end up getting one access violation after the other:
http://python.net/crew/theller/ctypes/tutorial.html

Another way is to wrap the C/C++ code to build (compile) a DLL that can be used by Python like any other module. About one third of the Python documentation actually covers this topic:
http://docs.python.org/ext/ext.html
Again, a short example how to do a such a wrapper.

#include <Python.h>
#include "structmember.h"

static PyObject* my_function( PyObject *self, PyObject *args )
{
    const char* _filename;

    if ( ! PyArg_ParseTuple( args, "s", &_filename ) ) return NULL;

    // do something in C++, for example calling other function
    // and return some values

    return Py_BuildValue( "i", 0 );
}

static PyMethodDef foo_methods[ ] = {
    { "myFunc", ( PyCFunction ) my_function, METH_VARARGS, "" },
    { NULL }  /* Sentinel */       
};

PyMODINIT_FUNC initfoo( void )
{
    ( void ) Py_InitModule( "foo", foo_methods );
}

Both ways are quite powerfull tools, but I am afraid they require some reading by yourself. Hope this helps for getting started,
enn0x

from ctypes tut above :

I started learning C++ weeks ago, using MinGW (Dev-C++ IDE).
In my case, it’s always exported as ordinal indices. I just realized a simple way to export a function name “as is”, as follows :

  1. force the linker to use an altered .def file (say hacked-DefFile.def) by adding this linking option :
    –def hacked-DefFile.def
  2. hacked-DefFile.def came from the original .def file
    original .def example :
EXPORTS
;	DllClass::getGLvendor()
	_ZN8DllClass11getGLvendorEv @ 1
;	DllClass::getGLversion()
	_ZN8DllClass12getGLversionEv @ 2
;	DllClass::getMaxLights()
	_ZN8DllClass12getMaxLightsEv @ 3
;	DllClass::getGLrenderer()
	_ZN8DllClass13getGLrendererEv @ 4
;	DllClass::getMaxTexSize()
	_ZN8DllClass13getMaxTexSizeEv @ 5
;	DllClass::getMaxTexStages()
	_ZN8DllClass15getMaxTexStagesEv @ 6
;	DllClass::getMaxClipPlanes()
	_ZN8DllClass16getMaxClipPlanesEv @ 7
;	DllClass::getPointAAmaxRange()
	_ZN8DllClass18getPointAAmaxRangeEv @ 8
;	DllClass::getPointAAgranularity()
	_ZN8DllClass21getPointAAgranularityEv @ 9
;	DllClass::getLineWidthAAgranularity()
	_ZN8DllClass25getLineWidthAAgranularityEv @ 10

hacked-DefFile.def :

EXPORTS
;	DllClass::getGLvendor()
	getGLvendor = _ZN8DllClass11getGLvendorEv @ 1
;	DllClass::getGLversion()
	getGLversion = _ZN8DllClass12getGLversionEv @ 2
;	DllClass::getMaxLights()
	getMaxLights = _ZN8DllClass12getMaxLightsEv @ 3
;	DllClass::getGLrenderer()
	getGLrenderer = _ZN8DllClass13getGLrendererEv @ 4
;	DllClass::getMaxTexSize()
	getMaxTexSize = _ZN8DllClass13getMaxTexSizeEv @ 5
;	DllClass::getMaxTexStages()
	getMaxTexStages = _ZN8DllClass15getMaxTexStagesEv @ 6
;	DllClass::getMaxClipPlanes()
	getMaxClipPlanes = _ZN8DllClass16getMaxClipPlanesEv @ 7
;	DllClass::getPointAAmaxRange()
	getPointAAmaxRange = _ZN8DllClass18getPointAAmaxRangeEv @ 8
;	DllClass::getPointAAgranularity()
	getPointAAgranularity = _ZN8DllClass21getPointAAgranularityEv @ 9
;	DllClass::getLineWidthAAgranularity()
	getLineWidthAAgranularity = _ZN8DllClass25getLineWidthAAgranularityEv @ 10

in this line : _ZN8DllClass11getGLvendorEv @ 1,
_ZN8DllClass11getGLvendorEv is DllClass::getGLvendor function, which is available as index 1 (@ 1)
so by setting :
getGLvendor = _ZN8DllClass11getGLvendorEv, we can access this function using getGLvendor reference, as clear and simple as expected.

my defFileHacker.py :

# the original .def filename
filename = 'libOGL2.def'
# the exported dll class
DllClass = 'DllClass'

origDefFile = open(filename,'r')
lineList = origDefFile.readlines()
origDefFile.close()

exportedNames=[]

filename='hacked-'+filename
hackedDefFile = open(filename,'w')
for i in range(len(lineList)):
    pos = lineList[i].rfind('::')
    if pos>0:
       pos+=2
       name = lineList[i][pos:]
       bracketPos = name.rfind('(')
       if bracketPos>0:
          name = name[:bracketPos]
#        print name
       if name!=DllClass and name!='~'+DllClass:
          _beg=lineList[i+1].find('_')
          lineList[i+1]= '%s%s = %s' %( lineList[i+1][:_beg], name, lineList[i+1][_beg:] )
          exportedNames.append("'"+name+"',")
hackedDefFile.writelines(lineList)
hackedDefFile.close()

exportedNamesFile = open('exportedNames.txt','w')
exportedNamesFile.writelines('\n'.join(exportedNames))
exportedNamesFile.close()

the result are :

  1. the hacked .def file (newly created), and
  2. exportedNames.txt : includes all exported functions names.

simple Panda Python sample :

from direct.gui.OnscreenText import OnscreenText
import direct.directbase.DirectStart

from ctypes import *
OGLlib=cdll.OGL2

def OSD(parent, text1,text2,tab,z):
    OnscreenText(
       parent=parent, text=text1, pos=(-tab,-z*.05), scale=.045, align=0
       )
    OnscreenText(
       parent=parent, text=text2, pos=(0,-z*.05), scale=.045, align=0
       )

# JUST TO MAINTAIN ORDER OF APPEARANCE, use a sequence
wanted=(
  'Vendor','getGLvendor',
  'Renderer','getGLrenderer',
  'Driver version','getGLversion',
  'Maximum texture size','getMaxTexSize',
  'Texture stages','getMaxTexStages',
  'Clip planes','getMaxClipPlanes',
  'Lights','getMaxLights',
  'Point Anti-Alias max range','getPointAAmaxRange',
  'Point Anti-Alias Granularity','getPointAAgranularity',
  'Line Anti-Alias Granularity','getLineWidthAAgranularity',
)

print '''
#######################################################
##                                                   ##
##        SOME GRAPHICS HARDWARE INFORMATION         ##
##                                                   ##
#######################################################'''
parent=aspect2d.attachNewNode('')
for d in range(0,len(wanted),2):
    funct = getattr(OGLlib,wanted[d+1])
    val = c_char_p(funct()).value
    OSD(parent, wanted[d],':  '+val, .47, d*.5)
    print '%28s : %s' %(wanted[d],val)
parent.setZ(-parent.getBounds().getCenter()[2])

console output :

Actually, my favorite way to do it is to insert my classes into the panda source tree, and then recompile panda. That way, panda’s mechanisms will take care of it for you. I find it fairly convenient.

As of panda 1.4.0, I created a directory panda/src/skel that contains some sample classes that don’t do anything, but which give you a starting point — it’s easy to insert your own methods into these classes. Then you can rename them, then you can copy them if you need more classes, and you can sort of build from there.

sure, you’re an expert and I’m just get started, so I’m still playing around with ctypes, at least I don’t have to re-build everything again and again.

[EDIT]
my barrier was – according to Python v2.4.3 docs – that I need .lib -> .a (MinGW import lib) converter, and I couldn’t find one. That’s why I fell back to ctypes.
But later, I realized that libpython24.a is already included in the dist. OMG, why couldn’t I see this before ?
Well, now I think I can do things more correctly, and yes it’s getting simpler and fun, at least I get types converter (num->str) for free. :smiley: