Python ctypes调用clib代码示例

通过c/c++编写动态链接库,再用Python调用写好的函数,可以有效利用c/c++程序的高效与Python的便捷。Python的ctypes库提供了相对便捷的clib调用。

下述代码在Python3中成功运行,但是Python2会报错,原因尚未深究。

C++

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define true 1
#define false 0

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;  
}

typedef struct StructPointerTest{
    char name[20];
    int age;
    int arr[3];
    StructPointerTest* next;
}StructPointerTest, *StructPointer; 

// 重要,因为使用g++编译时函数名会改变,比方print_msg(const char*)
// 会编译成函数名 print_msg_char,这会导致python调用这个函数的时候
// 找不到对应的函数名,只有加了 extern "C",才会以C语言的方式进行
// 编译,这样不会改变函数名字
extern "C" {  
    TestLib obj;  
    void display() {  
        obj.display();   
    }  
    void display_int() {  
        obj.display(2);   
    }
    void print_int_lst(const int* l, const int size) {
        cout << l[size-1] << endl;
    }
    void change_int_lst(int* l, const int size) {
        l[size-1] = 100;
    }
    void print(const char* s) {  
        cout << s << endl; 
    }
    float returnfloat(){
        float i=1;
        return i;
    }
    double returndouble(){
        double i=1;
        return i;
    }
    StructPointer returnstructure(){
        StructPointer p = (StructPointer)malloc(sizeof(StructPointerTest));
        StructPointer p2 = (StructPointer)malloc(sizeof(StructPointerTest));
        strcpy(p->name, "Joe");
        p->age = 20;
        p->arr[0] = 3;
        p->arr[1] = 5;
        p->arr[2] = 10;
        strcpy(p2->name, "John");
        p2->age = 40;
        p2->arr[0] = 30;
        p2->arr[1] = 50;
        p2->arr[2] = 100;
        p2->next = 0;
        p->next = p2;

        return p;
    }
}

Python

#! /bin/Python3.8/bin/python3
# _*_ encoding : utf-8 _*_

import ctypes

# |     ctypes     |     C                                   | Python                     |
# | -------------- | --------------------------------------- | ---------------------------|
# | c_char         | char                                    | 1-character string         |
# | c_wchar        | wchar_t                                 | 1-character unicode string |
# | c_byte         | char                                    | int/long                   |
# | c_ubyte        | unsigned char                           | int/long                   |
# | c_bool         | bool                                    | bool                       |
# | c_short        | short                                   | int/long                   |
# | c_ushort       | unsigned short                          | int/long                   |
# | c_int          | int                                     | int/long                   |
# | c_uint         | unsigned int                            | int/long                   |
# | c_long         | long                                    | int/long                   |
# | c_ulong        | unsigned long                           | int/long                   |
# | c_longlong     | __int64 or longlong                     | int/long                   |
# | c_ulonglong    | unsigned __int64 or unsigned long long  | int/long                   |
# | c_float        | float                                   | float                      |
# | c_double       | double                                  | float                      |
# | c_longdouble   | long double float                       | float                      |
# | c_char_p       | char *                                  | string or None             |
# | c_wchar_p      | wchar_t *                               | unicode or None            |
# | c_void_p       | void *                                  | int/long or None           |


so = ctypes.cdll.LoadLibrary

lib = so("./clib.so")

print('display()')
lib.display()

print('\ndisplay(100)')
lib.display_int(100)

print('\ndisplay(100)')
lib.display_int(100)

# Python向clib传递字符串时需要decode,否则会只传递第一个字符(实测表现)
print("\nPrint Test")
lib.print(b"Test")

print("\nPrint Test with encode")
lib.print("Test".encode())

print("\nPass [1,2,3,4] and print the last element")
ints = [1, 2, 3, 4]
intArray4 = ctypes.c_int * len(ints)
parameter_array = intArray4(*ints)
lib.print_int_lst(parameter_array, len(ints))

print("\nPass [1,2,3,4] and change the last element as 100")
ints = [1, 2, 3, 4]
intArray = ctypes.c_int * len(ints)
parameter_array = intArray(*ints)
lib.change_int_lst(parameter_array, len(ints))
print(parameter_array)
for i in range(0, len(parameter_array)):
    print( parameter_array[i]+2, end=" ")
print()

print("\nReceive return")
lib.returnfloat.restype = ctypes.c_float
lib.returndouble.restype = ctypes.c_float
print(isinstance(lib.returnfloat(), float))
print(lib.returnfloat())
print(lib.returnfloat()+1)
print(lib.returndouble())

print("\nReceive structure return")
class StructPointer(ctypes.Structure):
    pass
StructPointer._fields_ = [
    ("name", ctypes.c_char * 20),
    ("age", ctypes.c_int),
    ("arr", ctypes.c_int * 3),
    ("next", ctypes.POINTER(StructPointer))
]
lib.returnstructure.restype = ctypes.POINTER(StructPointer)
p = lib.returnstructure()
print(p.contents.name.decode())
print(p.contents.age)
print(len(p.contents.arr))
print(p.contents.arr)
print(p.contents.arr[0])
print(p.contents.arr[1])
print(p.contents.arr[2])
print(p.contents.next)
print(p.contents.next.contents.name.decode())
# 目前尚未找到python判断空指针的方法
print(p.contents.next.contents.next)

编译与调用

g++ -shared clib.cpp -o clib.so -fPIC

chmod +x caller.py
./caller.py
posted @ 2021-08-23 15:07  esctrionsit  阅读(340)  评论(0编辑  收藏  举报