2009-09-05 5 views
11

Common Lisp에서 C++ 코드를 호출 할 수있는 방법이 있는지 궁금하다. (가능하면, 가능하면 SBCL에, 가능하지 않다면, 가능하다면 Clozure, CLisp 또는 ECL로).Common Lisp에서 C++ (C가 아님)을 호출 하시겠습니까?

C++은 숫자 계산을 위해 내부 루프라고 부르기 때문에 호출이 빠르면 좋을 것입니다. 만 CFFI의 C 지원은 상당히 완전한 입니다 글을 쓰는 시점에서하지만, C++ 지원은 존재입니다

"개념은 다른 언어로 일반화 될 수 있습니다

CFFI은이 기능을 지원하지 않는 것 같습니다 에서 일하는."

(설명서의 4 장)

++ C를 언급하지 않는 SBCL 설명서 중 하나; 실제로 C 인터페이스는 유닉스 세계의 공용어의 일종이기 때문에

이 장에서는 다른 프로그램과 일반 의 라이브러리, C 프로그램과 라이브러리 (과에 SBCL의 인터페이스를 설명했다.)

C++ 코드는 OO와 연산자 오버로딩을 사용하므로 g ++로 컴파일해야합니다.

그리고 내가 아는 한, C++ main() 함수를 가지고 C 함수에 대한 래퍼를 작성할 수 있지만 그 반대의 방법은 없습니다. 사실입니까?

어쨌든 ...이 작업을 수행 할 수있는 방법이 있습니까?

감사합니다.

답변

6

아, 잠깐!

사용할 수있는 trick가있는 것 같습니다!

나는 래퍼 함수를 ​​extern "C"를 선언, C++의 래퍼를 쓰기 :

#include "lib.h" 

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

C와 C++ 모두에서 호출 할 수있는 헤더 파일 lib.h은 다음과 같습니다

#if __cplusplus 
extern "C" { 
#endif 

int lib_operate (int i, double *x); 

#if __cplusplus 
} 
#endif 

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

이 장난감 예를 들어 작동하는 것 같다 :

그런 다음 컴파일! :-)

그래서 Common Lisp에서는 libstdC++를로드 한 후 래퍼를 호출합니다.

어쨌든 답변 해 주셔서 감사합니다.

16

컴파일 한 후 대부분의 C++ 함수는 실제로 일반 C 함수 호출로 종결됩니다. 함수 오버로딩 및 기타 기능으로 인해 C++ 컴파일러는 비슷하게 명명 된 함수를 구별하기 위해 name mangling을 사용합니다. 객체 덤프 유틸리티와 C++ 컴파일러에 대한 충분한 지식이 주어지면 외부 세계에서 직접 C++ 코드를 호출 할 수 있습니다.

그러나 Lisp와 C++ 코드 사이에 C 호환 레이어를 작성하는 것이 더 쉬울 수도 있습니다. 당신은 할 것이라고 같이 extern "C"를 사용하여 :

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

이것은 외부 소스에서 호출 할 수 있도록 new_Foo() 함수가 호출 규칙을 C를 수행합니다.

+0

두 번째 옵션은 좋지만 C++ 코드 (광산이 아닙니다)는 연산자 오버로딩을 사용합니다. 나는 extern "C"operator +를 decalare 할 수 없다고 생각합니다. ... 첫 번째 옵션은 사용되는 컴파일러에 따라 다르지만 좋은 제안입니다! – Jay

+1

외부에서 오버로드 된 연산자를 호출 할 수 있습니다. API를 사용하여 조금 창의적이어야합니다.예를 들면 다음과 같습니다. 'extern "C"void add_Foo (Foo * result, const Foo * foo1, const Foo * foo2) {* result = * foo1 + * foo2; }' –

+3

모든 C++ 함수가 "일반 C로 종결되지"않습니다. 멤버 함수는 일반적으로 mangling이라는 이름 외에도 다른 호출 규칙을 사용합니다. (나는 x86에서 MSVC를 믿고, 'this' 매개 변수는 레지스터에 전달되지만 C 함수의 모든 매개 변수는 스택에 전달됩니다.) – jalf

13

이름 변환과는 별도로 C 함수 대신 C++ 함수를 호출 할 때의 주된 차이점은 암시 적으로 멤버 함수에 전달되는 포인터와 같은 '숨겨진'기능입니다. C 런타임 레이어는 이러한 암시 적 유형 변환 및 기타 재미있는 C++ 기능에 대해 알지 못하므로 C 인터페이스를 통해 C++을 호출하려는 경우 필요할 경우 이러한 기능을 위조해야 할 수 있습니다.

당신이 전화를하려는 목적과 필요한 데이터에 적어도 무효 *을 보유 할 수 있다고 가정하면, 당신은 당신이 C 만들 경우 C 호출에

matrix->multiply(avector); 

를 호출 ++ 다음 C 저하 될 수 있습니다 래퍼 함수 :

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

은 분명히 matrix_multiply 함수는 C++ 소스 코드에 앉아와 같은 컴파일하지만 외부 세계에 대한 C 인터페이스를 노출하지 것이다. 불투명 한 포인터와 상호 작용할 수있는 한 위의 번역 shim에서는 정상입니다.

분명히이 문제는 이와 같은 문제에 대한 가장 우아한 해결책은 아니지만 나는 너 같은 상황에서 과거에 사용 해왔다.

다른 옵션은 추가 매개 변수를 사용하여 C 호출로 처리하고 필요한 모든 정보를 직접 제공하여 C++ 호출을 직접 작성하는 것이지만 컴파일러 관련 코드의 영역으로 사용자를 매우 빠르게 이동시킵니다. 기본적으로 C++ 객체에 대한 불투명 포인터를 계속 보유 할 수는 있지만 호출 할 함수의 맹 글링 된 이름을 찾아야합니다. 그 함수 이름을 얻었 으면 this 포인터 (위의 예제에서 C++와 semi-implicit로 암시 적 임)와 올바른 매개 변수를 제공 한 다음 함수를 호출해야합니다. 그것은 할 수 있지만 언급 한 바와 같이, 컴파일러의 영역과 컴파일러 버전의 고유 한 동작 에까지 깊이 관여합니다.

3

C++ ABI에 따라 래퍼 (위 lib_operate)가 발생할 수있는 C++ 예외를 어떻게 든 처리해야 할 수 있습니다. ABI가 테이블 드라이브 예외 처리를 수행하는 경우 처리되지 않은 예외가 (Lisp) 프로세스를 중단시킵니다. 대신 동적 등록을 수행하면 잘못된 점을 발견하지 못할 수도 있습니다. 어느 쪽이든, 그것은 나쁘다.

또는 래핑 된 코드에 대해 no-throw 보증이있는 경우이 모든 것을 무시할 수 있습니다.

+0

나는 동의한다. 그러나 그것에 대해 무엇을해야합니까? –

관련 문제