0

可以ctypes用析构函数包装返回 C++ 类的对象(不是指针/引用)的函数吗?下面的示例在调用时会出现段错误lib.init_point_by_value

foo.cpp

#include <iostream>

extern "C" {

using namespace std;

struct Point {
    int x;
    int y;
    ~Point();
};

Point::~Point() {
     cout << "Point destructor called" << endl;
}

Point init_point_by_value(int x, int y) {
    cout << "init_point_by_value called" << endl;
    Point p;
    p.x = x;
    p.y = y;
    return p;
}

Point& init_point_by_ref(int x, int y) {
    cout << "init_point_by_ref called" << endl;
    Point* p = new Point;
    p->x = x;
    p->y = y;
    return *p;
}

void cleanup_point(Point* point) {
    cout << "cleanup_point called" << endl;
    if (point) {
        delete point;
    }
}

}

foo.py

import ctypes


class Point(ctypes.Structure):

    _fields_ = [
        ('x', ctypes.c_int),
        ('y', ctypes.c_int),
    ]


def setup_lib(lib_path):
    lib = ctypes.cdll.LoadLibrary(lib_path)
    lib.cleanup_point.argtypes = [ctypes.POINTER(Point)]

    lib.init_point_by_value.argtypes = [ctypes.c_int, ctypes.c_int]
    lib.init_point_by_value.restype = ctypes.POINTER(Point)

    lib.init_point_by_ref.argtypes = [ctypes.c_int, ctypes.c_int]
    lib.init_point_by_ref.restype = ctypes.POINTER(Point)

    return lib


lib = setup_lib('./foolib.so')

p1 = lib.init_point_by_ref(3, 4)
lib.cleanup_point(p1)

# seg faults
p2 = lib.init_point_by_value(5, 6)
lib.cleanup_point(p2)

编译并运行它:

g++ -c -fPIC foo.cpp -o foo.o && g++ foo.o -shared -o foolib.so && python foo.py 

输出:

init_point_by_ref called
cleanup_point called
Point destructor called
init_point_by_value called
Segmentation fault (core dumped)
4

1 回答 1

1

在启用警告的情况下编译,我得到:

x.cpp(17): warning C4190: 'init_point_by_value' has C-linkage specified, but returns UDT 'Point'
    which is incompatible with C

这是由于对象具有析构函数。删除析构函数,它应该接受它。

另一个问题是返回类型init_point_by_value不正确。它不是一个POINTER(Point),而只是一个Point

lib.init_point_by_value.restype = Point

最后,不要尝试释放按值返回的对象。

修复结果如下(针对我的 Windows 系统稍作调整):

测试.cpp

#include <iostream>

#define API __declspec(dllexport) // Windows-specific export
extern "C" {

using namespace std;

struct Point {
    int x;
    int y;
};

API Point init_point_by_value(int x, int y) {
    cout << "init_point_by_value called" << endl;
    Point p;
    p.x = x;
    p.y = y;
    return p;
}

API Point& init_point_by_ref(int x, int y) {
    cout << "init_point_by_ref called" << endl;
    Point* p = new Point;
    p->x = x;
    p->y = y;
    return *p;
}

API void cleanup_point(Point* point) {
    cout << "cleanup_point called" << endl;
    if (point) {
        delete point;
    }
}

}

测试.py

import ctypes

class Point(ctypes.Structure):
    _fields_ = [
        ('x', ctypes.c_int),
        ('y', ctypes.c_int),
    ]

def setup_lib(lib_path):
    lib = ctypes.cdll.LoadLibrary(lib_path)
    lib.cleanup_point.argtypes = [ctypes.POINTER(Point)]

    lib.init_point_by_value.argtypes = [ctypes.c_int, ctypes.c_int]
    lib.init_point_by_value.restype = Point

    lib.init_point_by_ref.argtypes = [ctypes.c_int, ctypes.c_int]
    lib.init_point_by_ref.restype = ctypes.POINTER(Point)

    return lib

lib = setup_lib('test')

p1 = lib.init_point_by_ref(3, 4)
print(p1.contents.x,p1.contents.y)
lib.cleanup_point(p1)

p2 = lib.init_point_by_value(5, 6)
print(p2.x,p2.y)

输出

init_point_by_ref called
3 4
cleanup_point called
init_point_by_value called
5 6
于 2018-06-26T01:11:19.097 回答