개발/C++

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

MinorMan 2020. 9. 26. 00:46
반응형

<질문>

나는이 사실을 알고.

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를 사용하고 있습니다


<답변1>

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(untyped_ptr);
    delete typed_ptr;
 }

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

<답변2>

나는 다음과 같은 방식으로 할 것입니다.

(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 

#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 

#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

편집하다:

일부 Linux 배포판에서는 마지막 컴파일 명령에 -laaa 및 -lstdc ++가 필요할 수도 있습니다. @AlaaM 덕분입니다. 관심을 끌기 위해


<답변3>

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


<답변4>

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

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


<답변5>

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


<답변6>

#include 

//////////////
// 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(thisPtr);
      classPtr->dump ();
    }
  }
  static void *deleteA (void *thisPtr)
  {
    // destroy the A class
    if (thisPtr != NULL)
    {
      delete (static_cast(thisPtr));
    }
  }
}

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

  dumpA (handle);
  deleteA (handle);

  return 0;
}

<답변7>

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

외부“C”int Mycpp 함수 ()

{

// 여기에 코드 입력

반환 0;

}

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

반응형