热门话题

Php 研究室

python 的 C语言中调用方法    作者:xieaotian发表于2008-08-21 15:19:43

Lots more details on writing wrappers From DANSE Table of contents [showhide] 1 Simple examples1.1 Convert input 1.2 Call the function 1.3 Convert the output 1.4 Complete wrapper example 2 class instances3 aggregate types3.1 Example: Unpacking a Python Dictionary 3.2 Example: Creating a Python list out of C++ variables Every wrapper function does three things: Converts the inputs from Python objects to C or C++ objects, calls the C++ function, converts the output to a Python object with the result and return it. Let's first run through these steps with numbers and strings, for which there's an immediate connection between Python and C++ types; we can use a function, PyArg_ParseTuple, which is built in to the API. Later examples look at tasks like working with C++ class instances and using aggregate Python types such as dictionaries and lists. [edit]Simple examples [edit]Convert input Every wrapper has the same signature as every other: PyObject * wrap_a_function(PyObject *, PyObject * args) PyObject (and everything else around here with the Py-prefix) is defined for us in the API. The first pointer argument, used for some highly specific purpose, is generally ignored. (When you do need it, you'll need a lot more help than I can give). The second argument is a PyTuple (a "subtype" of PyObject) that holds the tuple of arguments that the user called the function with in Python. //One integer:int a;int ok = PyArg_ParseTuple(args, "i",&a); if(!ok) return 0; //Two integersint a, b;int ok = PyArg_ParseTuple(args, "ii",&a, &b); if(!ok) return 0; //One integer, a string, two doublesint anint;char * astring; // don't allocate memory!double dub1, dub2;int ok = PyArg_ParseTuple(args, "isdd", &anint, &astring, &dub1, &dub2); if(!ok) return 0; A complete list of what can go into the format string is given in the extension documentation (look in the "ext" subdirectory of the "doc" directory in your Python distribution, or look online at http://docs.python.org/ext/ext.html ). In the current documentation, you want section 1.7, "Extracting Parameters in Extension Functions". PyArg_ParseTuple() checks the types of the objects in the tuple "args" against the types given in the format string. If there's a discrepancy, it sets an appropriate exception and returns a null pointer; The Python interpreter regards the null pointer as failure and raises an exception. So if you're using PyArg_ParseTuple(), most of the error checking is done for you! Once PyArg_ParseTuple has succesfully returned, do any additional processing or checking of the input data that's appropriate. For example, Python has no unsigned type, so you might need to make sure that an integer is greater than zero. [edit]Call the function It's your function, call it. [edit]Convert the output Convert the output to a Python object. The API gives a function called Py_BuildValue; here are some examples: // return an integerPyObject *py_result = Py_BuildValue("i", result); // return a stringPyObject *py_result = Py_BuildValue("s", astring); [edit]Complete wrapper example Here's a complete example of wrapping a function that takes a double, a string, and an int (in that order) and returns an int. We expect that the arguments will come in the order string, int, double. PyObject *wrap_bogus(PyObject *, PyObject * args){ //One: get the arguments from Python int anint = 0; double adub = 0; char * astring = 0; int ok = PyArg_ParseTuple( args, "sid", &astring, &anint, &adub); if(!ok) return 0; //do any checking of arguments here... //Two: make the function call int result = bogus( adub, astring, anint); //do any postprocessing of the result ... //Three: build a Python object to return return PyBuildValue("i",result);} [edit]class instances How do you use C++ classes from Python? You wrap the class's constructor(s) to dynamically allocate an instance of a C++ class and pass a handle back to Python. That handle can then be used with other wrappers for class methods. We get the arguments to the constructor from the Python API, create the object using new, and return a pointer to that object to the interpreter. Use the API function PyCObject_FromVoidPtr to return the pointer to Python. Here's an example with a real (if trivial) class called Numbers: class Numbers{public: Numbers(int first, double second) : one( first), two(second){} double NumMemberMult(void){ return m_first*m_second;}private: int m_first; double m_second;}; Here's a wrapper that creates a new instance of Numbers: PyObject *wrap_new_Numbers(PyObject *, PyObject* args){ //First, extract the arguments from a Python tuple int arg1; double arg2; int ok = PyArg_ParseTuple(args,"id",&arg1,&arg2); if(!ok) return 0; //Second, dynamically allocate a new object Numbers *newnum = new Numbers(arg1, arg2); //Third, wrap the pointer as a "PyCObject" and // return that object to the interpreter return PyCObject_FromVoidPtr( newnum, PyDelNumbers);} Look familiar? This wrapper has essentially the same structure as wrap_bogus(). That's because ALL wrappers have this structure. The pointer to the dynamically allocated object, newnum, goes out of scope as soon as wrap_new_Numbers() returns. The only thing keeping this from being the mega-classic memory leak is that the Python interpreter has an object that will keep track of the address of the new object. The interpreter tracks that object for us, and when we lose interest in it (its reference count goes to zero), the interpreter will call a C++ function to delete the object. The second slot in PyCObject_AsVoidPtr() is a pointer to a function with return type void and one void * argument. So the overall signature of PyCObject_AsVoidPtr is: PyObject * PyCObject_FromVoidPtr( void *, void (*DeleteFunction)(void*)); You must supply the function pointed to (in this example called PyDelNumbers). It has the "delete" corresponding to the "new" in the wrapper. Here's the definition of that function: static void PyDelNumbers(void *ptr){ //std::cout<<"Called PyDelNumbers()\n"; //Good to see once Numbers * oldnum = static_cast(ptr); delete oldnum; return;} The user can't actually do anything in the interpreter with the pointer, except send it back to the C++ library to do some other stuff, like call a C++ class method on it, or give it as an argument to another function. Here's an example of wrapping a class method. PyObject *wrap_Numbers_MemberMult(PyObject *, PyObject* args){ // First, get the PyCObject from the args tuple PyObject *pynum = 0; int ok = PyArg_ParseTuple( args, "O", &pynum); //"O" is for Object if(!ok) return NULL; // Convert the PyCObject to a void pointer: void * temp = PyCObject_AsVoidPtr(pynum); // Cast the void pointer to a Numbers pointer: Numbers * thisnum = static_cast(temp); //Second, make the function call double result = thisnum->NumMemberMult(); //Third, build & return a Python float object return Py_BuildValue("d",result);} All you have to do is get the PyCObject out of the tuple, then extract and cast the pointer to the appropriate type, and then use it, then bundle up the result and send it back to the interpreter. [edit]aggregate types Okay, so how about working with Python's aggregate types like lists and dictionaries? The API convenience functions that we use to go back and forth between simple Python objects and C objects don't have a fixed way to handle more complex objects like sequences. This probably makes sense: there's more than one way to represent a dictionary in C++, and maybe even more than one sensible way. We'll have to implement our own conversions, which really only means we have to look at the extensive API support to see what functions exist for working with dictionaries, lists, etc. [edit]Example: Unpacking a Python Dictionary Here's an example that takes a Python dictionary and extracts its contents. All you have to do is extract the dictionary from the args tuple, and get to work. This code has lots more checking of types than previous examples, where the API did that behind the scenes. It assumes that you've sent in a dictionary with strings as keys and floating point numbers as values. PyObject * wrap_dict(PyObject *, PyObject *args){ PyObject * dict = 0; int ok = PyArg_ParseTuple(args, "O", &dict); if (!ok) return 0; // is the object really a dictionary? if ( !PyDict_Check(dict)) { // set except context for TypeError PyErr_SetString( PyExc_TypeError, "That was not a dictionary!"); // tell interpreter to raise an exception return 0; } //PyDict_Keys returns a PyList with the keys: PyObject * key_list = PyDict_Keys(dict); int list_size = PyList_Size(key_list); // use STL vector to store numbers from dictionary std::vector my_numbers( list_size); //Get keys and corresponding values from the dictionary for( int i=0; i "<

回复主题
Copyright © 2008-2010 版权所属:中国Python联盟 www.okpython.com
京ICP备08012290号 村长QQ:81356625 E-mail:xieaotian@163.com