2009-09-05 33 views
11

Tôi tự hỏi nếu có một số cách để gọi mã C++ từ Common Lisp (tốt nhất là portably, và nếu không, tốt nhất là trong SBCL, và nếu không, tốt, sau đó Clozure, CLisp hoặc ECL).Gọi C++ (không phải C) từ Common Lisp?

C++ sẽ được gọi bên trong vòng lặp để tính toán số, vì vậy sẽ rất tuyệt nếu cuộc gọi nhanh.

CFFI dường như không hỗ trợ này:

"Khái niệm này có thể được khái quát hóa để ngôn ngữ khác; tại thời điểm văn bản, chỉ hỗ trợ C CFFI là khá hoàn chỉnh, nhưng C++ hỗ trợ là phúc làm việc trên."

(chương 4 của cuốn cẩm nang)

thủ SBCL của không đề cập đến C++ hoặc; nó thực sự nói

Chương này mô tả giao diện SBCL của các chương trình và thư viện (và C, vì giao diện C là một loại ngôn ngữ chung của thế giới Unix, các chương trình và các thư viện trong chung khác.)

Mã C++ sử dụng OO và quá tải toán tử, vì vậy nó thực sự cần được biên dịch bằng g ++.

Và theo như tôi biết, tôi có thể có hàm C++ main() và viết trình bao bọc cho hàm C, nhưng không phải là cách khác - đúng không?

Dù sao ... Có cách nào để thực hiện việc này không?

Cảm ơn bạn!

Trả lời

6

Ồ, đợi đã!

Dường như có một số trick Tôi có thể sử dụng!

tôi viết một wrapper trong C++, tuyên bố chức năng wrapper extern "C":

#include "lib.h" 

extern "C" int lib_operate (int i, double *x) { 
... 
} 

Các lib.h tập tin tiêu đề, có thể được gọi từ cả hai C và C++, là:

#if __cplusplus 
extern "C" { 
#endif 

int lib_operate (int i, double *x); 

#if __cplusplus 
} 
#endif 

Sau đó biên dịch với:

g++ -c lib.cpp 
gcc -c prog.c 
gcc lib.o prog.o -lstdc++ -o prog 

vẻ để làm việc cho một ví dụ đồ chơi! :-)

Vì vậy, trong Common Lisp tôi sẽ gọi trình bao bọc sau khi tải libstdC++.

Dù sao, cảm ơn bạn đã trả lời!

16

Sau khi biên dịch, hầu hết các hàm C++ thực sự đun sôi xuống các cuộc gọi hàm C thông thường. Do quá tải hàm và các tính năng khác, trình biên dịch C++ sử dụng name mangling để phân biệt giữa các hàm được đặt tên tương tự. Với một tiện ích kết xuất đối tượng và kiến ​​thức đầy đủ về trình biên dịch C++ của bạn, bạn có thể gọi mã C++ trực tiếp từ thế giới bên ngoài.

Mặc dù vậy, bạn có thể thấy dễ dàng hơn khi viết một lớp tương thích C giữa Lisp và mã C++ của bạn. Bạn sẽ làm điều đó bằng extern "C" như thế này:

extern "C" Foo *new_Foo(int x) 
{ 
    return new Foo(x); 
} 

Điều này làm cho new_Foo() chức năng theo các C gọi hội nghị để bạn có thể gọi nó là từ các nguồn bên ngoài.

+0

Tùy chọn thứ hai sẽ tốt, nhưng mã C++ (không phải của tôi) sử dụng quá tải toán tử. Tôi cho rằng tôi không thể decalare một nhà điều hành "C" bên ngoài ... Tùy chọn đầu tiên phụ thuộc vào trình biên dịch đang được sử dụng, nhưng là một gợi ý tốt! – Jay

+1

Bạn vẫn có thể gọi các toán tử quá tải từ bên ngoài, bạn chỉ cần có một chút sáng tạo với API của mình.Ví dụ: 'extern" C "void add_Foo (kết quả Foo *, const Foo * foo1, const Foo * foo2) {* result = * foo1 + * foo2; } ' –

+3

Không phải tất cả các hàm C++ đều" đun sôi xuống C ". Các hàm thành viên thường sử dụng một quy ước gọi khác ngoài tên mangling. (Tôi tin vào MSVC trên x86, tham số 'this' này được truyền trong một thanh ghi, trong khi tất cả các tham số trong hàm C được truyền trên stack) – jalf

13

Sự khác biệt chính trong việc gọi hàm C++ thay vì hàm C ngoài tên mangling là các tính năng 'ẩn' như con trỏ này được chuyển hoàn toàn đến các hàm thành viên. Lớp thời gian chạy C không biết gì về các chuyển đổi kiểu ngầm và các tính năng thú vị khác của C++, vì vậy nếu bạn định gọi C++ qua giao diện C, bạn có thể phải giả mạo các tính năng này nếu cần.

Giả sử rằng bạn có thể nắm giữ ít nhất một void * đến đối tượng bạn có ý định gọi và dữ liệu mà nó đòi hỏi, bạn có thể làm suy giảm C sau ++ gọi

matrix->multiply(avector); 

một cuộc gọi C nếu bạn tạo một C hàm wrapper:

extern "C" 
void matrix_multiply(void *cpp_matrix, void *cpp_vector) { 
    reinterpret_cast<matrix_type *>(cpp_matrix)->multiply(reinterpret_cast<vector_type *>(cpp_vector); 
} 

Rõ ràng hàm matrix_multiply sẽ nằm trong mã nguồn C++ và được biên dịch như vậy nhưng nó đưa ra giao diện C cho thế giới bên ngoài. Miễn là bạn có thể tương tác với các con trỏ mờ đục, bạn sẽ ổn với các phần dịch thuật ở trên.

Phải thừa nhận rằng đây không nhất thiết là giải pháp thanh lịch nhất cho một vấn đề như thế này nhưng tôi đã sử dụng nó trong quá khứ trong các tình huống như của bạn.

Tùy chọn khác sẽ là thực hiện cuộc gọi C++ trực tiếp bằng cách xử lý chúng như C cuộc gọi với các tham số bổ sung và cung cấp tất cả thông tin được yêu cầu, nhưng điều đó sẽ đưa bạn vào lĩnh vực mã trình biên dịch cụ thể rất nhanh. Về cơ bản, bạn vẫn sẽ giữ các con trỏ mờ đục đối với các đối tượng C++, nhưng bạn phải tìm ra tên bị xáo trộn của hàm mà bạn muốn gọi. Một khi bạn đã có tên hàm đó, bạn sẽ phải cung cấp cho con trỏ này (được ngầm định trong C++ và bán ngầm trong ví dụ trên) và các tham số chính xác và sau đó gọi hàm. Nó có thể được thực hiện nhưng như đã đề cập, đặt bạn sâu vào lĩnh vực biên dịch và thậm chí cả phiên bản trình biên dịch cụ thể.

3

Tùy thuộc vào C++ ABI của bạn, trình bao bọc của bạn (lib_operate ở trên) có thể cần bằng cách nào đó xử lý bất kỳ ngoại lệ C++ nào có thể xảy ra. Nếu ABI của bạn thực hiện xử lý ngoại lệ bảng-ổ đĩa, các ngoại lệ chưa được giải quyết sẽ chỉ làm hỏng quy trình (Lisp). Nếu nó thay vì đăng ký động, bạn có thể thậm chí không nhận thấy rằng bất cứ điều gì đã đi sai. Dù bằng cách nào, nó là xấu.

Hoặc, nếu bạn có bảo đảm không ném cho mã được bao bọc, bạn có thể bỏ qua tất cả điều này.

+0

Tôi đồng ý. Nhưng phải làm gì với nó? –