1. 程式人生 > >python與C++的相互呼叫(2)

python與C++的相互呼叫(2)

上文簡單介紹了python,以及在C中進行python模組的匯入、函式、類介面的獲得等比較基本的操作。接下來我們考慮:當我們已經獲得了函式的介面之後,我們就應該能夠對他進行呼叫了,接下來我們就來說一說函式的引數和返回值的問題。上文已經說了在python的世界裡一切都是以PyObject為基類的,那麼我們可以大膽的猜測,在python與C的函式介面中,入參和返回值都是PyObject*型別的。那麼,問題就變成了如何將C中的簡單型別轉換成 PyObject*型別了,至此,我們應該去查查CPython的介面函數了。

       在CPython的介面函式中,可以作為python的函式呼叫方式有PyAPI_FUNC(PyObject *) PyEval_CallFunction(PyObject *obj, const char *format, ...),這個函式第一個引數就是函式的Python物件,後面就是引數列表,具體的該函式呼叫的時候有點類似於C的printf()函式。具體可以看個例子:

        PyObject *presult = PyEval_CallFunction(pFunc,"ss","5","5")

        在這行程式碼中,pFunc是函式的PyObject物件,“ss”對應的是const char *format,後面兩個是具體的引數。對應printf的話,就是printf("%s%s","5',"5")。大致就是這麼個意思,他這邊的引數標識是沒有%做修飾的,從這裡可以看出來,s對應的就是字串,具體的對應關係比較多,本文後面會附錄所有的型別對應。

        現在,我們通過了PyEval_CallFunction呼叫了python的函式,獲得了返回值,但是這個返回值是PyObject* 的,現在我們碰到的問題是,如何把這個物件轉化成我們的需要的資料型別,這裡Python給了一組相關的函式:

         PyAPI_FUNC(char *) PyString_AsString(PyObject *);

         PyAPI_FUNC(long) PyInt_AsLong(PyObject *);

         PyAPI_FUNC(unsigned long) PyInt_AsUnsignedLongMask(PyObject *);

         通過這些函式,我們就能夠把這些Python物件轉換成我們需要的資料型別了。

下面就用一個大數相加的例子,來展現python和C混合程式設計的魔力吧。

add.py:

def add(x,y,base,outbase):

    a = int(x,base) + int(y,base)

    if (outbase == 8):

        return str(oct(a))

    elif (outbase == 10):

        return str(a)

    elif (outbase == 16):

        return str(hex(a))

    else:

        return None

cpp:

#include "stdafx.h"

#include <Python.h>

int _tmain(int argc, _TCHAR* argv[])

{

  Py_Initialize();

  if ( !Py_IsInitialized() ) 

  { 

    return -1; 

  } 

  PyRun_SimpleString("import add"); 

  PyObject *pName,*pMoudle,*pDict,*pFunc;

  pName = PyString_FromString("add");

  pMoudle = PyImport_Import(pName);

  if (!pMoudle)

  {

    printf("get moudle handle error");

    return -1;

  }

  pDict = PyModule_GetDict(pMoudle); 

  if ( !pDict ) 

  { 

    printf("get moudledict handle error");

    return -1; 

  }

  pFunc = PyDict_GetItemString(pDict,"add"); 

  if ( !pFunc || !PyCallable_Check(pFunc) ) 

  { 

    printf("can't find function [add]"); 

    getchar(); 

    return -1; 

  } 

  PyObject *presult =     PyEval_CallFunction(pFunc,"ssii","12345678ABCDEF123456789","ABCDEF12345678ABCDEF12345678",16,10);

  char *pout = PyString_AsString(presult);

  printf(pout);

  system("pause");

  return 0;

}

各位見到了吧:在C中很複雜的大數相加,在python和C的混合程式設計下,是不是變得異常簡單了呢,當然,大數相加可以這麼算,所有的大數運算都可以這麼實現,大家可以去嘗試一下。

以後碰到一些在C中需要寫很複雜的邏輯的,但是在python中能有很好的解決方案,大家都可以嘗試去這麼實現,相信會給你帶來不一樣的程式設計體驗。

附錄

型別的轉換標識:

"s" (string or Unicode object) [char *]

Convert a Python string or Unicode object to a C pointer to a character string. You must not provide storage for the string itself; a pointer to an existing string is stored into the character pointer variable whose address you pass. The C string is null-terminated. The Python string must not contain embedded null bytes; if it does, a TypeError exception is raised. Unicode objects are converted to C strings using the default encoding. If this conversion fails, an UnicodeError is raised.

"s#" (string, Unicode or any read buffer compatible object) [char *, int]

This variant on "s" stores into two C variables, the first one a pointer to a character string, the second one its length. In this case the Python string may contain embedded null bytes. Unicode objects pass back a pointer to the default encoded string version of the object if such a conversion is possible. All other read buffer compatible objects pass back a reference to the raw internal data representation.

"z" (string or None) [char *]

Like "s", but the Python object may also be None, in which case the C pointer is set to NULL.

"z#" (string or None or any read buffer compatible object) [char *, int]

This is to "s#" as "z" is to "s".

"u" (Unicode object) [Py_UNICODE *]

Convert a Python Unicode object to a C pointer to a null-terminated buffer of 16-bit Unicode (UTF-16) data. As with "s", there is no need to provide storage for the Unicode data buffer; a pointer to the existing Unicode data is stored into the Py_UNICODE pointer variable whose address you pass.

"u#" (Unicode object) [Py_UNICODE *, int]

This variant on "u" stores into two C variables, the first one a pointer to a Unicode data buffer, the second one its length.

"es" (string, Unicode object or character buffer compatible object) [const char *encoding, char **buffer]

This variant on "s" is used for encoding Unicode and objects convertible to Unicode into a character buffer. It only works for encoded data without embedded NULL bytes.

The variant reads one C variable and stores into two C variables, the first one a pointer to an encoding name string (encoding), the second a pointer to a pointer to a character buffer (**buffer, the buffer used for storing the encoded data) and the third one a pointer to an integer (*buffer_length, the buffer length).

The encoding name must map to a registered codec. If set to NULL, the default encoding is used.

PyArg_ParseTuple() will allocate a buffer of the needed size using PyMem_NEW(), copy the encoded data into this buffer and adjust *buffer to reference the newly allocated storage. The caller is responsible for calling PyMem_Free() to free the allocated buffer after usage.

"es#" (string, Unicode object or character buffer compatible object) [const char *encoding, char **buffer, int *buffer_length]

This variant on "s#" is used for encoding Unicode and objects convertible to Unicode into a character buffer. It reads one C variable and stores into two C variables, the first one a pointer to an encoding name string (encoding), the second a pointer to a pointer to a character buffer (**buffer, the buffer used for storing the encoded data) and the third one a pointer to an integer (*buffer_length, the buffer length).

The encoding name must map to a registered codec. If set to NULL, the default encoding is used.

There are two modes of operation:

If *buffer points a NULL pointer, PyArg_ParseTuple() will allocate a buffer of the needed size using PyMem_NEW(), copy the encoded data into this buffer and adjust *buffer to reference the newly allocated storage. The caller is responsible for calling PyMem_Free() to free the allocated buffer after usage.

If *buffer points to a non-NULL pointer (an already allocated buffer), PyArg_ParseTuple() will use this location as buffer and interpret *buffer_length as buffer size. It will then copy the encoded data into the buffer and 0-terminate it. Buffer overflow is signalled with an exception.

In both cases, *buffer_length is set to the length of the encoded data without the trailing 0-byte.

"b" (integer) [char]

Convert a Python integer to a tiny int, stored in a C char.

"h" (integer) [short int]

Convert a Python integer to a C short int.

"i" (integer) [int]

Convert a Python integer to a plain C int.

"l" (integer) [long int]

Convert a Python integer to a C long int.

"c" (string of length 1) [char]

Convert a Python character, represented as a string of length 1, to a C char.

"f" (float) [float]

Convert a Python floating point number to a C float.

"d" (float) [double]

Convert a Python floating point number to a C double.

"D" (complex) [Py_complex]

Convert a Python complex number to a C Py_complex structure.

"O" (object) [PyObject *]

Store a Python object (without any conversion) in a C object pointer. The C program thus receives the actual object that was passed. The object's reference count is not increased. The pointer stored is not NULL.

"O!" (object) [typeobject, PyObject *]

Store a Python object in a C object pointer. This is similar to "O", but takes two C arguments: the first is the address of a Python type object, the second is the address of the C variable (of type PyObject *) into which the object pointer is stored. If the Python object does not have the required type, TypeError is raised.

"O&" (object) [converteranything]

Convert a Python object to a C variable through a converter function. This takes two arguments: the first is a function, the second is the address of a C variable (of arbitrary type), converted to void *. The converter function in turn is called as follows:

status = converter(objectaddress);

where object is the Python object to be converted and address is the void * argument that was passed to PyArg_ConvertTuple(). The returned status should be 1 for a successful conversion and 0 if the conversion has failed. When the conversion fails, the converter function should raise an exception.

"S" (string) [PyStringObject *]

Like "O" but requires that the Python object is a string object. Raises TypeError if the object is not a string object. The C variable may also be declared as PyObject *.

"U" (Unicode string) [PyUnicodeObject *]

Like "O" but requires that the Python object is a Unicode object. Raises TypeError if the object is not a Unicode object. The C variable may also be declared as PyObject *.

"t#" (read-only character buffer) [char *, int]

Like "s#", but accepts any object which implements the read-only buffer interface. The char * variable is set to point to the first byte of the buffer, and the int is set to the length of the buffer. Only single-segment buffer objects are accepted; TypeError is raised for all others.

"w" (read-write character buffer) [char *]

Similar to "s", but accepts any object which implements the read-write buffer interface. The caller must determine the length of the buffer by other means, or use "w#" instead. Only single-segment buffer objects are accepted; TypeError is raised for all others.

"w#" (read-write character buffer) [char *, int]

Like "s#", but accepts any object which implements the read-write buffer interface. The char * variable is set to point to the first byte of the buffer, and the int is set to the length of the buffer. Only single-segment buffer objects are accepted; TypeError is raised for all others.

"(items)" (tuple) [matching-items]

The object must be a Python sequence whose length is the number of format units in items. The C arguments must correspond to the individual format units in items. Format units for sequences may be nested.

Note: Prior to Python version 1.5.2, this format specifier only accepted a tuple containing the individual parameters, not an arbitrary sequence. Code which previously caused TypeError to be raised here may now proceed without an exception. This is not expected to be a problem for existing code.

It is possible to pass Python long integers where integers are requested; however no proper range checking is done -- the most significant bits are silently truncated when the receiving field is too small to receive the value (actually, the semantics are inherited from downcasts in C -- your mileage may vary).

A few other characters have a meaning in a format string. These may not occur inside nested parentheses. They are:

"|"

Indicates that the remaining arguments in the Python argument list are optional. The C variables corresponding to optional arguments should be initialized to their default value -- when an optional argument is not specified, PyArg_ParseTuple() does not touch the contents of the corresponding C variable(s).

":"

The list of format units ends here; the string after the colon is used as the function name in error messages (the ``associated value'' of the exception that PyArg_ParseTuple() raises).

";"

The list of format units ends here; the string after the colon is used as the error message instead of the default error message. Clearly, ":" and ";" mutually exclude each other.