C#과 C++ 사이의 Interop 완성하기 – Step 1.

wxWindows의 .NET용 포팅인 wx.NET의 소스 코드를 살펴보다가 재미있는 토픽을 한 가지 발견해서 글을 써올립니다.


C#은 System.Interop.DllImportAttribute 라는 속성을 사용해서 MS .NET에서는 DLL을, Mono의 경우 Win32 버전에서는 DLL을, Linux, Unix, Mac OS X 등의 OS에서는 Dynamic Library와의 Interop을 가능하게 해줍니다. Interop 할 수 있는 대상은 라이브러리 외부로 노출된 C 언어 스타일의 함수입니다. 하지만 C++은 C#에서 직접적으로 Interop을 성립시킬 수 없는데, 운영 체제와 컴파일러에 따라서 C++ 클래스를 참조하는 방식이 제각기 다르기 때문입니다.


지금 소개하는 방법은 매우 간단하면서도 생각보다 잘 작동하는 것 같습니다. 바로 C++의 멤버 메서드와 멤버 변수, 생성자와 소멸자에 대한 프록시 함수를 작성하는 방법입니다.


1. 시작하기에 앞서 간단한 C++ 클래스 정의하기



    // TestClass.h에 정의하는 내용입니다.


    class TestClass


    {


    public:


        void TestFunction(int value);


        int MyFunction(void);

    private:


        int m_myValue;

    public:


        int GetMyValue(void);


        void SetMyValue(int value);

    public:


        TestClass(int value);


    }

    // TestClass.cpp에 정의하는 내용입니다.


    #include <iostream>

    void TestClass::TestFunction(int value)


    {


        stdout << value;


    }

    int TestClass::MyFunction(void)


    {


        return 123;


    }

    int TestClass::GetMyValue(void)


    {


        return this->m_myValue;


    }

    void TestClass::SetMyValue(int value)


    {


        this->m_myValue = value;


    }

    TestClass::TestClass(int value)


    {


        SetMyValue(value);


    }



2. 생성자와 소멸자에 대한 프록시 함수 만들기


프록시 함수에서 생성자와 소멸자의 정의는 반드시 이루어져야 합니다. 모든 프록시 함수는 클래스의 인스턴스를 레퍼런스 형식으로 참조해야 하기 때문에 레퍼런스를 할당하고 제거하는 일을 하는 함수가 있어야합니다. C#에서는 이러한 일을 하기 위해서 복잡한 시스템 API (예를 들자면 malloc()과 free()와 유사한) 를 다시 참조해야 하므로 C++에서 대신 처리하는 것이 바람직합니다. 또한 생성자는 C++ 클래스를 바인딩하는 C# 클래스의 생성자에서 호출되어야 하며 소멸자는 System.IDisposable 인터페이스의 구현 메서드인 Dispose() 메서드 또는 C# 클래스의 소멸자에서 호출되어야 합니다.


프록시 함수는 라이브러리 외부로 노출되어야 하는 함수입니다. Win32에서는 DLL로 제작해야 하며 이 때에는 .def 파일을 선언하여 컴파일러에게 전달하거나 Visual C++ 컴파일러를 사용한다면 extern “C” __declspec(dllexport) 키워드를 사용해도 됩니다.


생성자 프록시 함수는 매개 변수로는 생성자의 매개 변수 그대로를 받습니다. 반환값은 포인터로 반환하면 됩니다. 만약 여러개의 생성자가 정의된 클래스라면 프록시 함수도 이름을 달리해서 여러개로 정의해주면 됩니다.



    TestClass* TestClass_ctor(int value) // Creator


    {


        return new TestClass(value);


    }


소멸자 프록시 함수는 다른 매개 변수는 받을 필요 없이 포인터 매개 변수 하나만 받습니다. 단, 메모리 액세스 위반을 방지하기 위하여 받은 인스턴스가 이미 정리된 인스턴스인지 확인하는 단계를 거쳐서 delete 연산자로 소멸시키면 됩니다.



    void TestClass_dtor(TestClass* pInstance) // Destructor


    {


        if(pInstance != NULL)


             delete pInstance;

        pInstance = NULL;


    }


추가 정보: Visual C++ 컴파일러에서 클래스에 대해 __declspec(dllexport)를 사용하거나 MFC의 확장 매크로를 사용해도 이 강좌와 비슷한 디자인의 프록시 함수를 생성합니다. 하지만 Visual C++에 종속적인 내용이므로 다른 컴파일러에서는 사용할 수 없는 방법입니다. 참고해 주십시오. 또한 위와 같은 방법으로 생성된 프록시 함수들은 컴파일러가 임의로 이름을 붙이기 때문에 알아보기도 어렵고 일관성있는 규칙을 만들기가 어렵습니다. 그리고 이러한 함수를 찾기 위해서는 Dependency Walker라는 Visual Studio의 부가 도구를 사용해야 합니다. (Visual Studio Command Line에서 depends.exe 실행)


참고 자료: http://www.jprl.com/~jon/interop.html


일단 여기까지 글을 올립니다. 다음 글에서 멤버 함수와 멤버 변수에 관한 프록시를 만드는 방법을 소개하도록 하겠습니다.

댓글 남기기