source

C 소비용 C++ 클래스 API 포장

nicesource 2023. 10. 26. 21:15
반응형

C 소비용 C++ 클래스 API 포장

C/FFI 라이브러리에서 쉽게 사용할 수 있도록 DLL에서 랩핑하여 내보내야 하는 관련 C++ 클래스 세트가 있습니다.저는 이를 위한 "베스트 프랙티스"를 찾고 있습니다.예를 들어 객체 생성 및 자유화 방법, 기본 클래스 처리 방법, 대체 솔루션 등이 있습니다.

지금까지 제가 가진 몇 가지 기본 지침은 모든 파괴자를 포함하여 '이것' 포인터를 나타내는 여분의 공백* 인수를 가진 간단한 함수로 변환하는 것입니다.생성자는 원래 인수 목록을 유지할 수 있지만 개체를 나타내는 포인터를 반환해야 합니다.모든 메모리는 프로세스 전체에 동일한 할당 집합과 자유 루틴을 통해 처리되어야 하며 매크로 또는 다른 방식을 통해 핫 스왑이 가능해야 합니다.

각 공개 방법에 대해 C 함수가 필요합니다.
C 코드에서 클래스를 나타내려면 불투명 포인터도 필요합니다.
void* 및 기타 정보를 포함하는 구조를 구축할 수 있지만 void*를 사용하는 것이 더 간단합니다(예: 어레이를 지원하는 경우?).

Fred.h
--------------------------------

#ifdef  __cplusplus
class Fred
{
    public:
    Fred(int x,int y);
    int doStuff(int p);
};
#endif

//
// C Interface.
typedef void*   CFred;

//
// Need an explicit constructor and destructor.
extern "C" CFred  newCFred(int x,int y);
extern "C" void   delCFred(CFred);

//
// Each public method. Takes an opaque reference to the object
// that was returned from the above constructor plus the methods parameters.
extern "C" int    doStuffCFred(CFred,int p);

구현은 사소한 것입니다.
불투명 포인터를 프레드로 변환한 다음 메서드를 호출합니다.

CFred.cpp
--------------------------------

// Functions implemented in a cpp file.
// But note that they were declared above as extern "C" this gives them
// C linkage and thus are available from a C lib.
CFred newCFred(int x,int y)
{
    return reinterpret_cast<void*>(new Fred(x,y));
}

void delCFred(CFred fred)
{
    delete reinterpret_cast<Fred*>(fred);
}

int doStuffCFred(CFred fred,int p)
{
    return reinterpret_cast<Fred*>(fred)->doStuff(p);
}

로키 아스타리의 답은 매우 좋으나 샘플 코드는 C++ 클래스 안에 래핑 코드를 넣었습니다.저는 포장 코드를 별도의 파일로 작성하는 것을 선호합니다.그리고 랩핑 C 기능은 클래스 이름 앞에 붙이는 것이 더 좋은 스타일이라고 생각합니다.

다음의 블로그 게시물들은 그것을 하는 방법을 보여줍니다: http://blog.eikke.com/index.php/ikke/2005/11/03/using_c_classes_in_c.html

블로그가 폐기되어 결국 사라질 수도 있기 때문에 꼭 필요한 부분을 복사했습니다(Ikke's Blog의 크레딧).


우선 헤더 파일 하나를 사용하는 C++ 클래스가 필요합니다(Test.hh).

class Test {
    public:
        void testfunc();
        Test(int i);

    private:
        int testint;
};

1개의 구현 파일(Test.cc )

#include <iostream>
#include "Test.hh"

using namespace std;

Test::Test(int i) {
    this->testint = i;
}

void Test::testfunc() {
    cout << "test " << this->testint << endl;
}

이것은 기본적인 C++ 코드입니다.

그럼 접착제 코드가 필요합니다.이 코드는 C와 C++ 사이에 있는 것입니다.다시 헤더 파일이 하나 나왔습니다. (TestWrapper.h, C++ 코드가 포함되어 있지 않기 때문에 .)

typedef void CTest;

#ifdef __cplusplus
extern "C" {
#endif

CTest * test_new(int i);
void test_testfunc(const CTest *t);
void test_delete(CTest *t);
#ifdef __cplusplus
}
#endif

및 기능 구현(TestWrapper.cc , .cc는 C++ 코드를 포함하고 있으므로):

#include "TestWrapper.h"
#include "Test.hh"

extern "C" {

    CTest * test_new(int i) {
        Test *t = new Test(i);

        return (CTest *)t;
    }

    void test_testfunc(const CTest *test) {
        Test *t = (Test *)test;
        t->testfunc();
    }

    void test_delete(CTest *test) {
        Test *t = (Test *)test;

        delete t;
    }
}

내 경험에서 나온 몇 가지 의견:

  • 함수는 오류를 나타내기 위해 코드를 반환해야 합니다.오류 설명을 문자열 형태로 반환하는 기능이 있으면 유용합니다.다른 모든 반환 값은 모수를 벗어나야 합니다.

예:

C_ERROR BuildWidget(HUI ui, HWIDGET* pWidget);
  • 유효성에 대한 핸들 확인을 위해 구조물에 서명/핸들 포인터를 classes합니다.

예를 들어, 기능은 다음과 같이 보여야 합니다.

C_ERROR BuildWidget(HUI ui, HWIDGET* pWidget){
    Ui* ui = (Ui*)ui;
    if(ui.Signature != 1234)
    return BAD_HUI;
}
  • DLL의 메모리 할당 방식과 앱 소비 방식이 다를 수 있으므로 DLL에서 내보낸 함수를 사용하여 개체를 생성하고 릴리스해야 합니다.

예:

C_ERROR CreateUi(HUI* ui);
C_ERROR CloseUi(HUI hui); // usually error codes don't matter here, so may use void
  • 라이브러리 외부에 유지되는 데 필요한 버퍼 또는 기타 데이터에 대해 메모리를 할당하는 경우 이 버퍼/데이터의 크기를 지정합니다.이를 통해 사용자는 실제 크기를 알아보기 위해 내부를 해킹하지 않고도 디스크, DB 또는 원하는 곳에 저장할 수 있습니다.그렇지 않으면 사용자가 데이터를 알려진 크기의 바이트 배열로 변환하는 데만 사용할 자신의 파일 I/O api를 제공해야 합니다.

예:

C_ERROR CreateBitmap(HUI* ui, SIZE size, char** pBmpBuffer, int* pSize);
  • 만약 당신의 객체가 당신의 C++ 라이브러리 밖에서 어떤 전형적인 표현을 가지고 있다면, 이 표현으로 변환하는 평균을 제공하세요 (예를 들어, 당신이 어떤 클래스를 가지고 있다면)Image그리고 그것에 대한 접근을 제공합니다.HIMGhandle, window HBITMAP)로 변환 및 변환할 수 있는 기능을 제공합니다.이를 통해 기존 API와의 통합이 간소화됩니다.

예.

C_ERROR BitmapToHBITMAP(HUI* ui, char* bmpBuffer, int size, HBITMAP* phBmp);

첫째, 당신의 모든 메소드를 C 함수로 변환할 필요가 없을 수 있습니다.API를 단순화하고 C++ 인터페이스를 일부 숨길 수 있다면 C++ 로직을 뒤로 변경할 때 C API를 변경할 기회를 최소화할 수 있으므로 더 좋습니다.

따라서 해당 API를 통해 제공될 상위 수준의 추상화를 생각해 보십시오.설명한 void* 솔루션을 사용합니다.제가 보기에는 가장 적절합니다(또는 HANDLE으로 def void*를 입력합니다 :).

벡터(및 문자열::c_str)를 사용하여 C++가 아닌 API와 데이터를 교환합니다. (C++ Coding Standards의 가이드라인 #78, H. Sutter/ A.알렉산드르스쿠).

PS "시공자가 원래 주장 목록을 유지할 수 있다"는 것은 사실이 아닙니다.이것은 C 호환되는 인수 유형에만 해당됩니다.

PS2 물론 C ăt ălin을 듣고 인터페이스를 가능한 작고 간단하게 유지합니다.

이 내용은 C++ FAQ Lite에서 "C와 C++의 혼합"에 관심이 있을 수 있습니다.구체적으로 [32.8] C++ 클래스의 객체를 C 함수로/또는 C 함수로 전달하려면 어떻게 해야 합니까?

언급URL : https://stackoverflow.com/questions/1588788/wrapping-c-class-api-for-c-consumption

반응형