Talking about Python Examples--Python and C/C++ call each other

Reprint link: Talking about Python Examples 3 Python and C/C++ call each other

Table of contents

1. Problems

2. Python calls C/C++

1. Python calls the C dynamic link library

2. Python calls C++ (class) dynamic link library

3. Python calls C/C++ executable program

3. C/C++ calls Python


 

1. Problems


      The mutual calls between Python modules and C/C++ dynamic libraries will be involved in practical applications, so here is a summary.

2. Python calls C/C++


1. Python calls the C dynamic link library


        It is relatively simple for Python to call the C library. It is packaged into so without any packaging, and then called by python's ctypes.
(1) C language file: pycall.c

/***gcc -o libpycall.so -shared -fPIC pycall.c*/
#include <stdio.h>
#include <stdlib.h>
int foo(int a, int b)
{
  printf("you input %d and %d\n", a, b);
  return a+b;
}


(2) Compile and generate dynamic library libpycall.so with gcc: gcc -o libpycall.so -shared -fPIC pycall.c. When using g++ to compile the functions or methods in the code that generates the C dynamic library, you need to use extern "C" to compile.


(3) Python calls the file of the dynamic library: pycall.py

import ctypes
ll = ctypes.cdll.LoadLibrary 
lib = ll("./libpycall.so")  
lib.foo(1, 3)


(4) Running results:

 

2. Python calls C++ (class) dynamic link library


       It needs extern "C" to assist, that is to say, only C functions can be called, and methods cannot be called directly, but C++ methods can be parsed. Instead of using extern "C", the built dynamic link library does not have a symbol table for these functions.
(1) C++ class file: pycallclass.cpp

#include <iostream>
using namespace std;
 
class TestLib
{
    public:
        void display();
        void display(int a);
};
void TestLib::display() {
    cout<<"First display"<<endl;
}
 
void TestLib::display(int a) {
    cout<<"Second display:"<<a<<endl;
}
extern "C" {
    TestLib obj;
    void display() {
        obj.display(); 
      }
    void display_int() {
        obj.display(2); 
      }
}


(2) Compile with g++ to generate the dynamic library libpycall.so: g++ -o libpycallclass.so -shared -fPIC pycallclass.cpp.
(3) Python calls the file of the dynamic library: pycallclass.py

import ctypes
so = ctypes.cdll.LoadLibrary 
lib = so("./libpycallclass.so") 

lib.display()

lib.display_int(100)


(4) Running results:

3. Python calls C/C++ executable program


(1) C/C++ program: main.cpp

#include <iostream>
using namespace std;
int test()
{
    int a = 10, b = 5;
    return a+b;
}
int main()
{
    cout<<"---begin---"<<endl;
    int num = test();
    cout<<"num="<<num<<endl;
    cout<<"---end---"<<endl;
}


(2) Compile into a binary executable file: g++ -o testmain main.cpp.
(3) Python calling program: main.py

import commands
import os
main = "./testmain"
if os.path.exists(main):
    rc, out = commands.getstatusoutput(main)
    print 'rc = %d, \nout = %s' % (rc, out)

f = os.popen(main)  
data = f.readlines()  
f.close()  
print data

os.system(main)


(4) Running results:


4. Extend Python (C++ writes extension modules for Python)


       Any code that can be integrated or imported into other python scripts is called an extension. Extensions can be written in Python, or in compiled languages ​​such as C and C++. At the beginning of its design, Python considered to make the import mechanism of modules sufficiently abstract. It is so abstract that the code using the module cannot understand the specific implementation details of the module. The extensibility of Python has advantages: it is convenient to add new functions to the language, it is customizable, and the code can be reused.
       Creating an extension for Python requires three main steps: creating the application code, wrapping the code with boilerplate, and compiling and testing.
(1) Create application code 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
int fac(int n)
{
    if (n < 2) return(1); /* 0! == 1! == 1 */
    return (n)*fac(n-1); /* n! == n*(n-1)! */
}
 
char *reverse(char *s)
{
    register char t,                    /* tmp */
            *p = s,                     /* fwd */
            *q = (s + (strlen(s) - 1)); /* bwd */
 
    while (p < q)               /* if p < q */
    {
        t = *p;         /* swap & move ptrs */
        *p++ = *q;
        *q-- = t;
    }
    return(s);
}
 
int main()
{
    char s[BUFSIZ];
    printf("4! == %d\n", fac(4));
    printf("8! == %d\n", fac(8));
    printf("12! == %d\n", fac(12));
    strcpy(s, "abcdef");
    printf("reversing 'abcdef', we get '%s'\n", \
        reverse(s));
    strcpy(s, "madam");
    printf("reversing 'madam', we get '%s'\n", \
        reverse(s));
    return 0;
}


       There are two functions in the above code, one is the recursive factorial function fac(); the other reverse() function implements a simple string reversal algorithm, and its main purpose is to modify the incoming string so that its content It is completely reversed, but there is no need to apply for memory and then copy backwards.         (2) The code that wraps the
code interface with boilerplate is called "boilerplate" code, and it is an essential part of the interaction between the application code and the Python interpreter.
The template is mainly divided into 4 steps: a. Include the Python header file; b. Add a wrapper function such as PyObject* Module_func() for each function of each module; c. Add a type such as PyMethodDef ModuleMethods for each module The array of []; d, increase the module initialization function void initModule ().

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
int fac(int n)
{
    if (n < 2) return(1);
    return (n)*fac(n-1);
}
 
char *reverse(char *s)
{
    register char t,
            *p = s,
            *q = (s + (strlen(s) - 1));
 
    while (s && (p < q))
    {
        t = *p;
        *p++ = *q;
        *q-- = t;
    }
    return(s);
}
 
int test()
{
    char s[BUFSIZ];
    printf("4! == %d\n", fac(4));
    printf("8! == %d\n", fac(8));
    printf("12! == %d\n", fac(12));
    strcpy(s, "abcdef");
    printf("reversing 'abcdef', we get '%s'\n", \
        reverse(s));
    strcpy(s, "madam");
    printf("reversing 'madam', we get '%s'\n", \
        reverse(s));
    return 0;
}
 
#include "Python.h"
 
static PyObject *
Extest_fac(PyObject *self, PyObject *args)
{
    int num;
    if (!PyArg_ParseTuple(args, "i", &num))
        return NULL;
    return (PyObject*)Py_BuildValue("i", fac(num));
}
 
static PyObject *
Extest_doppel(PyObject *self, PyObject *args)
{
    char *orig_str;
    char *dupe_str;
    PyObject* retval;
 
    if (!PyArg_ParseTuple(args, "s", &orig_str))
        return NULL;
    retval = (PyObject*)Py_BuildValue("ss", orig_str,
        dupe_str=reverse(strdup(orig_str)));
    free(dupe_str);             #防止内存泄漏
    return retval;
}
 
static PyObject *
Extest_test(PyObject *self, PyObject *args)
{
    test();
    return (PyObject*)Py_BuildValue("");
}
 
static PyMethodDef
ExtestMethods[] =
{
    { "fac", Extest_fac, METH_VARARGS },
    { "doppel", Extest_doppel, METH_VARARGS },
    { "test", Extest_test, METH_VARARGS },
    { NULL, NULL },
};
 
void initExtest()
{
    Py_InitModule("Extest", ExtestMethods);
}


        The Python.h header file will be in the /usr/local/include/python2.x or /usr/include/python2.x directory on most Unix-like systems, and the system will generally know the path where the file is installed.
        Add a wrapper function, the module name is Extest, then create a wrapper function called Extest_fac(), use it in a Python script first import Extest, and then call Extest.fac(), when Extest.fac() is called, the wrapper function Extest_fac() will be called, the wrapper function accepts a Python integer parameter, converts it to a C integer, then calls the C fac() function, gets an integer return value, and finally converts the return value to Python The integer is returned as the result of the entire function call. The other two wrapper functions Extest_doppel() and Extest_test() are similar.
         The conversion from Python to C uses the PyArg_Parse* series of functions, int PyArg_ParseTuple(): convert the parameters passed from Python to C; int PyArg_ParseTupleAndKeywords() has the same function as PyArg_ParseTuple(), but at the same time parse keyword parameters; their usage is the same as that of C The sscanf function is very similar, it accepts a string stream, and parses it according to a specified format string, and puts the result into the variable pointed by the corresponding pointer. Their return value is 1, indicating that the parsing is successful, and returns A value of 0 indicates failure. The conversion function from C to Python is PyObject* Py_BuildValue(): convert the C data into an object or a group of objects in Python, and then return it; Convert the specified format into a Python object.
        Conversion code for data conversion between C and Python:

        

 

        Add an array of type PyMethodDef ModuleMethods[] for each module so that the Python interpreter can import and call them. Each array contains the name of the function in Python, the name of the corresponding wrapper function and a METH_VARARGS constant. METH_VARARGS indicates that the parameters are passed in as tuple. If you need to use the PyArg_ParseTupleAndKeywords() function to analyze named parameters, you also need to make this flag constant and the METH_KEYWORDS constant perform logical AND operation constants. At the end of the array, two NULLs are used to indicate the end of the function information list.
         The last part of all the work is the initialization function of the module, calling the Py_InitModule() function, and passing in the module name and the name of the ModuleMethods[] array so that the interpreter can correctly call the functions in the module.
(3) Compilation
        In order for new Python extensions to be created, they need to be compiled together with the Python library. The distutils package is used to compile, install and distribute these modules, extensions and packages.
        Create a setup.py file, and the main work of compiling is done by the setup() function:

#!/usr/bin/env python
 
from distutils.core import setup, Extension
 
MOD = 'Extest'
setup(name=MOD, ext_modules=[Extension(MOD, sources=['Extest2.c'])])


        The first argument to Extension() is the (complete) name of the extension, plus the full package name separated by '.' if the module is part of a package. The above-mentioned extensions are independent, so just write "Extest" as the name; the sources parameter is a file list of all source codes, and there is only one file Extest2.c. setup requires two parameters: a name parameter indicates which content to compile; another list parameter lists the objects to be compiled, and the above-mentioned thing to be compiled is an extension, so the value of the ext_modules parameter is set to the list of extension modules.
        Run the setup.py build command to start compiling our extension, prompting some information:

creating build/lib.linux-x86_64-2.6
gcc -pthread -shared build/temp.linux-x86_64-2.6/Extest2.o -L/usr/lib64 -lpython2.6 -o build/lib.linux-x86_64-2.6/Extest.so


(4) Import and test
         your extension will be created in the build/lib.* directory under the directory where the setup.py script is run, you can switch to that directory to test the module, or you can use the command to install it into Python Middle: python setup.py install, the corresponding information will be prompted.
         Test module:


(5) Macros for reference counting and thread-safe
       Python object reference counting: Py_INCREF(obj) increases the reference count of the object obj, and Py_DECREF(obj) decreases the reference count of the object obj. The two functions Py_INCREF() and Py_DECREF() also have a version that checks whether the object is empty first, Py_XINCREF() and Py_XDECREF() respectively.
      Programmers who compile extensions must be aware that the code may be run in a multi-threaded Python environment. These threads use two C macros, Py_BEGIN_ALLOW_THREADS and Py_END_ALLOW_THREADS. By isolating the code from the thread, the safety of running and non-running is guaranteed. The code wrapped by these macros will allow other threads to run.

 


3. C/C++ calls Python


       C++ can call Python scripts, so you can write some Python script interfaces for C++ to call. At least you can treat Python as a dynamic link library in text form, and you can change it when necessary, as long as you don’t change the interface. The disadvantage is that once the C++ program is compiled, it is not so convenient to change it.
(1) Python script: pytest.py

#test function
def add(a,b):
    print "in python function add"
    print "a = " + str(a)
    print "b = " + str(b)
    print "ret = " + str(a+b)
    return
 
def foo(a):
 
    print "in python function foo"
    print "a = " + str(a)
    print "ret = " + str(a * a)
    return 
 
class guestlist:
    def __init__(self):
        print "aaaa"
    def p():
      print "bbbbb"
    def __getitem__(self, id):
      return "ccccc"
def update():
    guest = guestlist()
    print guest['aa']
 
#update()


(2) C++ code:

/**g++ -o callpy callpy.cpp -I/usr/include/python2.6 -L/usr/lib64/python2.6/config -lpython2.6**/
#include <Python.h>
int main(int argc, char** argv)
{
    // 初始化Python
    //在使用Python系统前,必须使用Py_Initialize对其
    //进行初始化。它会载入Python的内建模块并添加系统路
    //径到模块搜索路径中。这个函数没有返回值,检查系统
    //是否初始化成功需要使用Py_IsInitialized。
    Py_Initialize();
 
    // 检查初始化是否成功
    if ( !Py_IsInitialized() ) {
        return -1;
    }
    // 添加当前路径
    //把输入的字符串作为Python代码直接运行,返回0
    //表示成功,-1表示有错。大多时候错误都是因为字符串
    //中有语法错误。
    PyRun_SimpleString("import sys");
    PyRun_SimpleString("print '---import sys---'"); 
    PyRun_SimpleString("sys.path.append('./')");
    PyObject *pName,*pModule,*pDict,*pFunc,*pArgs;
 
    // 载入名为pytest的脚本
    pName = PyString_FromString("pytest");
    pModule = PyImport_Import(pName);
    if ( !pModule ) {
        printf("can't find pytest.py");
        getchar();
        return -1;
    }
    pDict = PyModule_GetDict(pModule);
    if ( !pDict ) {
        return -1;
    }
 
    // 找出函数名为add的函数
    printf("----------------------\n");
    pFunc = PyDict_GetItemString(pDict, "add");
    if ( !pFunc || !PyCallable_Check(pFunc) ) {
        printf("can't find function [add]");
        getchar();
        return -1;
     }
 
    // 参数进栈
    PyObject *pArgs;
    pArgs = PyTuple_New(2);
 
    //  PyObject* Py_BuildValue(char *format, ...)
    //  把C++的变量转换成一个Python对象。当需要从
    //  C++传递变量到Python时,就会使用这个函数。此函数
    //  有点类似C的printf,但格式不同。常用的格式有
    //  s 表示字符串,
    //  i 表示整型变量,
    //  f 表示浮点数,
    //  O 表示一个Python对象。
 
    PyTuple_SetItem(pArgs, 0, Py_BuildValue("l",3));
    PyTuple_SetItem(pArgs, 1, Py_BuildValue("l",4));
 
    // 调用Python函数
    PyObject_CallObject(pFunc, pArgs);
 
    //下面这段是查找函数foo 并执行foo
    printf("----------------------\n");
    pFunc = PyDict_GetItemString(pDict, "foo");
    if ( !pFunc || !PyCallable_Check(pFunc) ) {
        printf("can't find function [foo]");
        getchar();
        return -1;
     }
 
    pArgs = PyTuple_New(1);
    PyTuple_SetItem(pArgs, 0, Py_BuildValue("l",2)); 
 
    PyObject_CallObject(pFunc, pArgs);
     
    printf("----------------------\n");
    pFunc = PyDict_GetItemString(pDict, "update");
    if ( !pFunc || !PyCallable_Check(pFunc) ) {
        printf("can't find function [update]");
        getchar();
        return -1;
     }
    pArgs = PyTuple_New(0);
    PyTuple_SetItem(pArgs, 0, Py_BuildValue(""));
    PyObject_CallObject(pFunc, pArgs);     
 
    Py_DECREF(pName);
    Py_DECREF(pArgs);
    Py_DECREF(pModule);
 
    // 关闭Python
    Py_Finalize();
    return 0;
} 


(3) Compile C++ into a binary executable file: g++ -o callpy callpy.cpp -I/usr/include/python2.6 -L/usr/lib64/python2.6/config -lpython2.6, compilation options need to be specified manually Python's include path and link path (Python version number depends on the specific situation).
(4) Running results:

 

Guess you like

Origin blog.csdn.net/qq_38295645/article/details/124416630