C에서 C ++ 함수를 호출하는 방법은 무엇입니까?


84

나는이 사실을 알고.

C ++에서 C 함수 호출 :

내 응용 프로그램이 C ++이고 C로 작성된 라이브러리에서 함수를 호출해야했다면 다음을 사용했을 것입니다.

//main.cpp

extern "C" void C_library_function(int x, int y);//prototype
C_library_function(2,4);// directly using it.

이것은 이름을 엉망으로 만들지 않을 것이며 C_library_function링커는 입력 * .lib 파일에서 동일한 이름을 찾고 문제가 해결됩니다.

C에서 C ++ 함수 호출 ???

하지만 여기서는 C로 작성된 대형 애플리케이션을 확장하고 있으며 C ++로 작성된 라이브러리를 사용해야합니다. C ++의 이름 변경이 여기서 문제를 일으키고 있습니다. 링커가 해결되지 않은 기호에 대해 불평하고 있습니다. 다른 많은 것들을 깨뜨리기 때문에 C 프로젝트에 C ++ 컴파일러를 사용할 수 없습니다. 탈출구는 무엇입니까?

그건 그렇고 나는 MSVC를 사용하고 있습니다




답변:


95

C ++ 코드의 기능을 노출하려면 C API를 만들어야합니다. 기본적으로 extern "C"로 선언되고 C ++ 라이브러리를 래핑하는 순수 C API (예 : 클래스를 사용하지 않음)가있는 C ++ 코드를 작성해야합니다. 그런 다음 생성 한 순수 C 래퍼 라이브러리를 사용합니다.

C가 객체 지향이 아니더라도 C API는 선택적으로 객체 지향 스타일을 따를 수 있습니다. 전의:

 // *.h file
 // ...
 #ifdef __cplusplus
 #define EXTERNC extern "C"
 #else
 #define EXTERNC
 #endif

 typedef void* mylibrary_mytype_t;

 EXTERNC mylibrary_mytype_t mylibrary_mytype_init();
 EXTERNC void mylibrary_mytype_destroy(mylibrary_mytype_t mytype);
 EXTERNC void mylibrary_mytype_doit(mylibrary_mytype_t self, int param);

 #undef EXTERNC
 // ...


 // *.cpp file
 mylibrary_mytype_t mylibrary_mytype_init() {
   return new MyType;
 }

 void mylibrary_mytype_destroy(mylibrary_mytype_t untyped_ptr) {
    MyType* typed_ptr = static_cast<MyType*>(untyped_ptr);
    delete typed_ptr;
 }

 void mylibrary_mytype_doit(mylibrary_mytype_t untyped_self, int param) {
    MyType* typed_self = static_cast<MyType*>(untyped_self);
    typed_self->doIt(param);
 }


1
최종 실행 파일을 어떻게 연결합니까? C 또는 C ++를 사용하십니까? (C를 사용할 때 링커가 C ++ 함수를 찾을 수 없었고, C ++를 사용할 때 링커가 std :: cout을 찾을 수 없었습니다.)
Zack

1
@Zack 종속성을 전 이적으로 포함하는 C ++ 컴파일러 / 링커로 정적 라이브러리를 만드는 경우 (링커에 의해 암시 적으로 링크 될 수있는 C ++ 표준 라이브러리 포함) 해당 정적 라이브러리를 C 코드에 연결할 수 있어야합니다. C 컴파일러 / 링커.
마이클 아론 SAFYAN

C ++에서 C로 템플릿을 내보낼 수 있습니까?
MarcusJ

내 라이브러리를 C ++ 및 C 코드로 호출해야하므로 "하이브리드"동작이 있습니다. 또한 링크 타임에 "undefined reference to"오류를 피하기 위해 *.cpp file함수를 #ifdef _cplusplus+ 로 래핑해야 extern "C"했습니다.
Coconop

41

다음과 같은 방식으로 수행합니다.

(MSVC로 작업하는 경우 GCC 컴파일 명령 무시)

aaa.h, aaa.cpp 파일에 정의 된 AAA 라는 C ++ 클래스 가 있고 AAA 클래스 에 C 코드에 대해 활성화하려는 sayHi (const char * name) 이라는 메서드가 있다고 가정 합니다.

클래스 AAA 의 C ++ 코드 -순수 C ++, 수정하지 않습니다.

aaa.h

#ifndef AAA_H
#define AAA_H

class AAA {
    public:
        AAA();
        void sayHi(const char *name);
};

#endif

aaa.cpp

#include <iostream>

#include "aaa.h"

AAA::AAA() {
}

void AAA::sayHi(const char *name) {
    std::cout << "Hi " << name << std::endl;
}

이 클래스를 C ++에 대해 정기적으로 컴파일합니다. 이 코드는 C 코드에서 사용될 것이라는 것을 "모릅니다". 명령 사용 :

g++ -fpic -shared aaa.cpp -o libaaa.so

이제 C ++에서도 C 커넥터를 만듭니다. aaa_c_connector.h, aaa_c_connector.cpp 파일에서 정의합니다 . 이 커넥터는 AAA 의 인스턴스를 사용하고 해당 메소드를 호출하는 AAA_sayHi (cosnt char * name) 라는 C 함수를 정의합니다 .

aaa_c_connector.h

#ifndef AAA_C_CONNECTOR_H 
#define AAA_C_CONNECTOR_H 

#ifdef __cplusplus
extern "C" {
#endif

void AAA_sayHi(const char *name);

#ifdef __cplusplus
}
#endif


#endif

aaa_c_connector.cpp

#include <cstdlib>

#include "aaa_c_connector.h"
#include "aaa.h"

#ifdef __cplusplus
extern "C" {
#endif

// Inside this "extern C" block, I can implement functions in C++, which will externally 
//   appear as C functions (which means that the function IDs will be their names, unlike
//   the regular C++ behavior, which allows defining multiple functions with the same name
//   (overloading) and hence uses function signature hashing to enforce unique IDs),


static AAA *AAA_instance = NULL;

void lazyAAA() {
    if (AAA_instance == NULL) {
        AAA_instance = new AAA();
    }
}

void AAA_sayHi(const char *name) {
    lazyAAA();
    AAA_instance->sayHi(name);
}

#ifdef __cplusplus
}
#endif

다시 일반 C ++ 컴파일 명령을 사용하여 컴파일합니다.

g++ -fpic -shared aaa_c_connector.cpp -L. -laaa -o libaaa_c_connector.so

이제 C 함수 AAA_sayHi (const char * name) 을 구현하는 공유 라이브러리 (libaaa_c_connector.so )가 있습니다. 이제 C 메인 파일을 만들고 함께 컴파일 할 수 있습니다.

main.c

#include "aaa_c_connector.h"

int main() {
    AAA_sayHi("David");
    AAA_sayHi("James");

    return 0;
}

C 컴파일 명령을 사용하여 컴파일 :

gcc main.c -L. -laaa_c_connector -o c_aaa

$ PWD를 포함하도록 LD_LIBRARY_PATH를 설정해야하며 실행 파일 ./c_aaa를 실행하면 예상되는 출력을 얻을 수 있습니다.

Hi David
Hi James

편집하다:

일부 리눅스 배포판에, -laaa그리고 -lstdc++또한 마지막 컴파일 명령이 필요할 수 있습니다. @AlaaM 덕분입니다. 관심을 끌기 위해


1
lib 링크 경로를 지정할 수도 있습니다.gcc usecpp.c -L. -laaa_c_connector -Wl,-rpath,. -o c_aaa
Metaphox

뭐야 usecpp.c?
Alaa M.

1
마지막 컴파일 줄은이어야합니다 gcc main.c -L. -laaa_c_connector -laaa -lstdc++ -o c_aaa. 을 참고 -laaa하고-lstdc+++
알라 M.

@AlaaM. 누군가 내 코드를 편집 main.c하고 usecpp.c. 방금 되돌 렸습니다 .... 다른 댓글에 대해서는 그대로 작동합니다. 두 번째 컴파일은 사용 -laaa하고 아마도 충분할 것입니다 (1.5 년 이상 전에 썼습니다. 내가 한 일을 잘 기억하지 못하지만 게시하기 전에 모든 줄을 테스트 한 것 같습니다)
SomethingSomething

1
좋습니다. 게시물 끝에 메모로 추가했습니다. 관심을 가져 주셔서 감사합니다!
SomethingSomething

6

이렇게하려면 C ++로 C 용 래퍼를 작성해야합니다. C ++는 이전 버전과 호환되지만 C는 이전 버전과 호환되지 않습니다.


5

C ++ API가 C 호환 (클래스, 템플릿 등 없음)이라고 가정하면 extern "C" { ... }다른 방법으로 진행할 때와 마찬가지로으로 래핑 할 수 있습니다 .

객체 및 기타 귀여운 C ++ 항목을 노출하려면 래퍼 API를 작성해야합니다.


정답이 아닙니다 ... C ++ 라이브러리를 다시 컴파일해야합니다.
Michael Aaron Safyan

오 안돼. C ++ API는 완전히 객체 지향적입니다.
claws

2
@claws, OOP 스타일 C 코드 만들기에 대한 내 기사를 참조하고 C 인터페이스가있는 해당 스타일을 사용하는 래퍼 라이브러리를 만들지 만 기본 C ++ 구현입니다. 그런 다음 C 인터페이스에 연결합니다.
Michael Aaron Safyan

당신은 (...하지 C쪽으로 아직) 상당의 물건을 꿀꺽 꿀꺽, pyboost 같은 래퍼 '도구', ...에서 살펴 봐야 할 수 있습니다
향하지 않음 xtofl

2

C ++ 함수를 extern "C"(일명 C 스타일 기호)로 내보내거나 .def 파일 형식을 사용하여 C ++ 라이브러리를 생성 할 때 C ++ 링커에 대한 데코 레이팅되지 않은 내보내기 기호를 정의하면 C 링커가 읽는 데 문제가 없습니다.


0
#include <iostream>

//////////////
// C++ code //
//////////////
struct A
{
  int i;
  int j;

  A() {i=1; j=2; std::cout << "class A created\n";}
  void dump() {std::cout << "class A dumped: " << i << ":" << j << std::endl;}
  ~A() {std::cout << "class A destroyed\n";}
};

extern "C" {
  // this is the C code interface to the class A
  static void *createA (void)
  {
    // create a handle to the A class
    return (void *)(new A);
  }
  static void dumpA (void *thisPtr)
  {
    // call A->dump ()
    if (thisPtr != NULL) // I'm an anal retentive programmer
    {
      A *classPtr = static_cast<A *>(thisPtr);
      classPtr->dump ();
    }
  }
  static void *deleteA (void *thisPtr)
  {
    // destroy the A class
    if (thisPtr != NULL)
    {
      delete (static_cast<A *>(thisPtr));
    }
  }
}

////////////////////////////////////
// this can be compiled as C code //
////////////////////////////////////
int main (int argc, char **argv)
{
  void *handle = createA();

  dumpA (handle);
  deleteA (handle);

  return 0;
}

2
답변에 몇 가지 설명을 추가해보십시오.
HMD

위의 질문에 대한 답변이 다른 사람들에게 더 유용한 답변을 만드는 방법에 대한 간략한 설명입니다 .
SOS

-3

함수 선언 앞에 extern "C"키워드를 붙일 수 있습니다.

extern“C”int Mycppfunction ()

{

// 여기에 코드 입력

반환 0;

}

더 많은 예를 보려면 "extern"키워드에 대해 Google에서 더 많이 검색 할 수 있습니다. 몇 가지만 더 할 필요가 있지만 Google에서 많은 예제를 얻는 것은 어렵지 않습니다.


하나의 설명이 아닌 세 개의 반대표. 언제나처럼 위대한 SO 커뮤니티.
Zimano 2019
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.