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