programing

객체 지향 C++ 코드용 C 래퍼 API 개발

itsource 2022. 8. 2. 23:57
반응형

객체 지향 C++ 코드용 C 래퍼 API 개발

기존의 C++ API를 둘러싼 일련의 C API를 개발하여 코어 로직(개체 지향 C++로 작성)에 액세스하려고 합니다.이는 기본적으로 C++ 로직을 다른 언어로 사용할 수 있도록 하는 글루 API가 될 것입니다.C를 객체 지향 C++로 감싸는 개념을 소개하는 좋은 튜토리얼, 책 또는 베스트 프랙티스는 무엇입니까?

이것은 수작업으로 실행하는 것은 그다지 어렵지 않지만, 인터페이스의 크기에 따라 다릅니다.C코드 내에서 C++라이브러리를 사용할 수 있도록 하는 것이었기 때문에 SWIG는 큰 도움이 되지 않았습니다(SWIG를 사용할 수 있을지도 모릅니다만, 저는 SWIG 전문가가 아니기 때문에 간단한 것은 아닌 것 같습니다).

결국 우리가 한 일은 다음과 같은 일을 했습니다.

  1. C에서는 모든 오브젝트가 불투명한 핸들로 전달됩니다.
  2. 생성자와 파괴자가 순수한 함수로 포장되어 있습니다.
  3. 멤버 함수는 순수한 함수입니다.
  4. 기타 빌트인은 가능한 경우 C 등가물에 매핑됩니다.

이러한 클래스(C++ 헤더)는

class MyClass
{
  public:
  explicit MyClass( std::string & s );
  ~MyClass();
  int doSomething( int j );
}

다음과 같은 C 인터페이스에 매핑합니다(C 헤더).

struct HMyClass; // An opaque type that we'll use as a handle
typedef struct HMyClass HMyClass;
HMyClass * myStruct_create( const char * s );
void myStruct_destroy( HMyClass * v );
int myStruct_doSomething( HMyClass * v, int i );

인터페이스의 실장은, 다음과 같습니다(C++ 송신원).

#include "MyClass.h"

extern "C" 
{
  HMyClass * myStruct_create( const char * s )
  {
    return reinterpret_cast<HMyClass*>( new MyClass( s ) );
  }
  void myStruct_destroy( HMyClass * v )
  {
    delete reinterpret_cast<MyClass*>(v);
  }
  int myStruct_doSomething( HMyClass * v, int i )
  {
    return reinterpret_cast<MyClass*>(v)->doSomething(i);
  }
}

캐스팅을 피하기 위해 원래 클래스에서 불투명한 핸들을 추출했습니다(현재 사용하고 있는 컴파일러에서는 동작하지 않는 것 같습니다).C가 수업을 지원하지 않기 때문에 손잡이를 구조화해야 합니다.

이것으로 기본적인 C 인터페이스를 얻을 수 있습니다.예외처리를 통합할 수 있는 한 가지 방법을 보여주는 보다 완전한 예를 원하시면 github에서 제 코드를 사용해 보십시오.https://gist.github.com/mikeando/5394166

여기서 중요한 것은 필요한 모든 C++ 라이브러리를 보다 큰 라이브러리에 올바르게 링크할 수 있도록 하는 것입니다.gcc(또는 clang)의 경우 이는 g++를 사용하여 최종 링크 단계를 수행하는 것을 의미합니다.

나는 마이클 앤더슨의 대답이 옳다고 생각하지만 나의 접근 방식은 다를 것이다.한 가지 더 걱정해야 할 것은 예외입니다.예외는 CABI의 일부가 아니기 때문에 예외는 C++ 코드를 지나도록 할 수 없습니다.헤더는 다음과 같습니다.

#ifdef __cplusplus
extern "C"
{
#endif
    void * myStruct_create( const char * s );
    void myStruct_destroy( void * v );
    int myStruct_doSomething( void * v, int i );
#ifdef __cplusplus
}
#endif

래퍼의 .cpp 파일은 다음과 같습니다.

void * myStruct_create( const char * s ) {
    MyStruct * ms = NULL;
    try { /* The constructor for std::string may throw */
        ms = new MyStruct(s);
    } catch (...) {}
    return static_cast<void*>( ms );
}

void myStruct_destroy( void * v ) {
    MyStruct * ms = static_cast<MyStruct*>(v);
    delete ms;
}

int myStruct_doSomething( void * v, int i ) {
    MyStruct * ms = static_cast<MyStruct*>(v);
    int ret_value = -1; /* Assuming that a negative value means error */
    try {
        ret_value = ms->doSomething(i);
    } catch (...) {}
    return ret_value;
}

한층 더 뛰어난 기능:MyStruct의 단일 인스턴스로 필요한 모든 것을 알고 있다면 API로 전달되는 void 포인터에 대처할 위험을 감수하지 마십시오.대신 다음과 같은 작업을 수행합니다.

static MyStruct * _ms = NULL;

int myStruct_create( const char * s ) {
    int ret_value = -1; /* error */
    try { /* The constructor for std::string may throw */
        _ms = new MyStruct(s);
        ret_value = 0; /* success */
    } catch (...) {}
    return ret_value;
}

void myStruct_destroy() {
    if (_ms != NULL) {
        delete _ms;
    }
}

int myStruct_doSomething( int i ) {
    int ret_value = -1; /* Assuming that a negative value means error */
    if (_ms != NULL) {
        try {
            ret_value = _ms->doSomething(i);
        } catch (...) {}
    }
    return ret_value;
}

이 API가 훨씬 안전합니다.

하지만 마이클이 언급했듯이 연결은 꽤 까다로울 수 있다.

도움이 되었으면 좋겠다

C++ 코드를 C에 노출시키는 것은 어렵지 않습니다. 단지 파사드 디자인 패턴을 사용합니다.

C++ 코드가 라이브러리에 내장되어 있다고 가정하면, C++ 라이브러리에 있는 하나의 C 모듈을 순수한 C 헤더 파일과 함께 라이브러리에 대한 파사드로 만들면 됩니다.C 모듈은 관련 C++ 함수를 호출합니다.

이렇게 하면 C 어플리케이션과 라이브러리가 노출한 C API에 완전히 액세스할 수 있습니다.

예를 들어, 이것은 Passide 모듈의 예시입니다.

#include <libInterface.h>
#include <objectedOrientedCppStuff.h>

int doObjectOrientedStuff(int *arg1, int arg2, char *arg3) {
      Object obj = ObjectFactory->makeCppObj(arg3); // doing object oriented stuff here
      obj->doStuff(arg2);
      return obj->doMoreStuff(arg1);
   }

그런 다음 이 C 함수를 API로 공개하고 Clib로 자유롭게 사용할 수 있습니다.

// file name "libIntrface.h"
extern int doObjectOrientedStuff(int *, int, char*);

이것은 분명히 의도된 예이지만 C++ 라이브러리를 C에 설명하는 가장 쉬운 방법입니다.

방향성에 대한 아이디어를 얻거나 SWIG를 직접 활용할 수 있다고 생각합니다.몇 가지 예를 검토하면 적어도 API를 다른 API로 정리할 때 어떤 것을 고려해야 하는지 알 수 있을 것 같습니다.그 운동은 유익할 수 있다.

SWIG는 C와 C++로 작성된 프로그램을 다양한 고급 프로그래밍 언어와 연결하는 소프트웨어 개발 도구입니다.SWIG는 Perl, PHP, Python, Tcl 및 Ruby와 같은 일반적인 스크립트 언어를 포함하여 다양한 유형의 언어와 함께 사용됩니다.지원되는 언어 목록에는 C#, Common Lisp(CLISP, Allegro CL, CFFI, UFFI), Java, Lua, Modula-3, OCAML, Octabe 및 R 등의 비스크립트 언어도 포함되어 있습니다.또한 해석 및 컴파일된 스킴 구현(Guile, MzScheme, Chicken)도 지원됩니다.SWIG는 높은 수준의 해석 또는 컴파일된 프로그래밍 환경, 사용자 인터페이스를 만들고 C/C++ 소프트웨어를 테스트 및 프로토타이핑하기 위한 도구로 가장 일반적으로 사용됩니다.SWIG는 해석 트리를 XML 및 Lisp s-expression 형식으로 내보낼 수도 있습니다.SWIG는 상업 및 비상업적 용도로 자유롭게 사용, 배포 및 수정할 수 있습니다.

을 '사물의 개념'으로.void *(C 지향 라이브러리에서는 종종 불투명 유형으로 표시됨) C++에서 알고 있는 모든 정보를 재사용합니다.

SWIG를 사용하는 것이 가장 좋은 답이라고 생각합니다.이것은 바퀴의 재창조를 피할 뿐만 아니라 신뢰할 수 있을 뿐만 아니라 문제를 해결하기보다는 개발의 연속성을 촉진합니다.

고주파 문제는 장기적인 해결책으로 해결할 필요가 있습니다.

언급URL : https://stackoverflow.com/questions/2045774/developing-c-wrapper-api-for-object-oriented-c-code

반응형