2012-02-24 35 views
6

Có một libx.so trong đó xuất khẩu 2 chức năng, và một struct,C struct bằng Python

typedef struct Tag { 
    int num; 
    char *name; 
}Tag; 

Tag *create(int n, char *n) 
{ 
    Tag *t = malloc(sizeof(Tag)); 
    t->num = n; 
    t->name = n; 
    return t; 
} 

void use(Tag *t) 
{ 
    printf("%d, %s\n", t->num, t->name); 
} 

Tôi muốn gọi create bằng Python và sau đó lưu Tag *res trả về bởi create, sau đó tôi sẽ gọi use và vượt qua các Tag *res lưu trước khi đến use, đây là nó (chỉ để chứng minh):

>>>libx = ctypes.CDLL("./libx.so") 
>>>res = libx.create(c_int(1), c_char_p("a")) 
>>>libx.use(res) 

Đoạn mã trên có thể sai, chỉ để chứng minh những gì tôi muốn làm.

Và vấn đề của tôi là, làm thế nào tôi có thể lưu kết quả được trả về bởi create? Bởi vì nó trả về một con trỏ đến một người dùng định nghĩa struct và tôi không muốn xây dựng đối tác của struct Tag bằng Python, sẽ c_void_p thực hiện thủ thuật?

CẬP NHẬT

Từ @ câu trả lời của David, tôi vẫn không hiểu một điều:

con trỏ (c_char_p("a")) chỉ có giá trị trong suốt thời gian của cuộc gọi đến create. Ngay sau khi tạo trả về thì con trỏ đó không còn giá trị nữa.

Và tôi gán c_char_p("a") để t->name trong create, khi cuộc gọi đến create kết thúc, là t->name một con trỏ tòn ten? Bởi vì theo các từ được trích dẫn, con trỏ đó không còn hợp lệ sau create. Tại sao c_char_p("a") không còn hợp lệ?

Trả lời

4

Mã C mà bạn trình bày chỉ đơn giản là không hoạt động. Bạn cần phải chính xác hơn nhiều về việc nhóm nào phân bổ và chịu trách nhiệm về bộ nhớ heap.

Trong ví dụ hiện tại của bạn, bạn chuyển c_char_p("a") tới mã C. Tuy nhiên, con trỏ đến bộ nhớ của ctypes chỉ hợp lệ trong thời lượng cuộc gọi đến create. Ngay sau khi create trả về thì con trỏ đó không còn hợp lệ nữa. Nhưng bạn đã lấy một bản sao của con trỏ bên trong create. Do đó, lệnh gọi tiếp theo đến use có thể bị lỗi.

Bạn sẽ cần phải lấy một bản sao nội dung của chuỗi đó và lưu trữ nó trong cấu trúc. Nếu bạn làm điều đó thì bạn có thể sử dụng an toàn libx.create.restype = c_void_p.

Nhưng nếu bạn muốn bộ nhớ bạn cấp phát được giải phóng, bạn sẽ phải cung cấp hàm destroy để khớp với hàm create. Với những thay đổi mã C sẽ trông như thế này:

Tag *create(int n, char *s) 
{ 
    Tag *t = malloc(sizeof(Tag)); 
    t->num = n; 
    t->name = strdup(s); 
    return t; 
} 

void destroy(Tag *t) 
{ 
    free(t->name); 
    free(t); 
} 

Các mã Python sẽ trông như thế này:

libx = ctypes.CDLL("./libx.so") 
libx.create.restype = c_void_p 
res = libx.create(c_int(1), c_char_p("a")) 
libx.use(res) 
libx.destroy(res) 
+0

tôi vừa bị vướng vào quần của tôi –

+0

Vâng, thưa bạn, có 3 điều tôi không hiểu. 1, 'con trỏ đến bộ nhớ của ctypes đó chỉ hợp lệ trong suốt thời gian của cuộc gọi để tạo.' Tại sao vậy? Tôi đoán đó là bởi vì sau khi cuộc gọi đến 'create',' ref '' 's (" a ")' s đếm đến 0, vì vậy '" a "' là rác thu thập, phải không? 2, trong mã của bạn, 'res' là một đối tượng' c_void_p', nhưng 'libx.use' lấy một' Tag * 'arg, tôi có thể chuyển' res' trực tiếp tới 'libx.use' mà không có bất kỳ phép đúc nào không? 3, tại sao tôi nên rõ ràng 'phá hủy (res)'? – Alcott

+0

Tôi chỉ chạy thử nghiệm, có vẻ như mã của tôi có thể hoạt động chính xác, thưa bạn. – Alcott

0

Python không tính tham khảo. Bạn sẽ phải sử dụng Py_INCREF() và bạn bè cho các đối tượng được trả về từ các thư viện "bên ngoài".

CẬP NHẬT: Tôi không biết về .so tải bằng python, có thể phương pháp được đề xuất bởi @David Hefferman thực hiện điều này một cách tự động.

UPDATE2: xóa tôi!

+1

Câu hỏi liên quan đến ctypes là giao diện chức năng ở nước ngoài, tương tự như pinvoke nếu bạn biết điều đó. 'Py_INCREF' chỉ đơn giản là không thích hợp. –

+0

Ok. Tôi sẽ xóa. – wildplasser