programing

C/C++ 라이브러리를 여러 클라이언트 언어로 사용할 수 있도록 설계하는 방법은 무엇입니까?

newsource 2023. 6. 14. 21:52

C/C++ 라이브러리를 여러 클라이언트 언어로 사용할 수 있도록 설계하는 방법은 무엇입니까?

다양한 플랫폼에서 많은 사람들이 사용할 수 있는 라이브러리를 코딩할 계획입니다.제대로 설계하려면 무엇을 고려해야 합니까?이 질문을 보다 구체적으로 설명하기 위해 마지막에 네 가지 "하위 질문"이 있습니다.

언어 선택

알려진 모든 요구 사항과 세부 사항을 고려하여, 저는 C 또는 C++로 쓰여진 도서관이 가야 할 길이라고 결론 내렸습니다.제 라이브러리의 주요 용도는 C, C++ Java SE로 작성된 프로그램일 것입니다. 하지만 Java ME, PHP, .NET, Objective C, Python, Ruby, bash 스크립트 등에서 사용해야 하는 이유도 생각할 수 있습니다.모두 대상으로 할 수는 없겠지만, 가능하다면 그렇게 하겠습니다.

요구 사항들

여기서 제 라이브러리의 전체 목적을 설명하는 것은 무리가 있겠지만, 이 질문에 중요한 몇 가지 측면이 있습니다.

  • 라이브러리 자체는 처음에는 작은 규모로 시작하지만, 앞으로는 매우 복잡해질 것이므로 여러 버전을 동시에 유지하는 것은 선택사항이 아닙니다.
  • 대부분의 복잡성은 도서관 내부에 숨겨져 있을 것입니다.
  • 라이브러리는 내부에서 많이 사용되는 개체 그래프를 구성합니다.라이브러리의 일부 클라이언트는 특정 개체의 특정 속성에만 관심이 있는 반면, 다른 클라이언트는 어떤 방식으로든 개체 그래프를 통과해야 합니다.
  • 클라이언트가 개체를 변경할 수 있으며 라이브러리에 이를 통지해야 합니다.
  • 라이브러리가 개체를 변경할 수 있으며, 해당 개체에 대한 핸들이 이미 있는 경우 클라이언트에 이를 통지해야 합니다.
  • 라이브러리는 여러 다른 호스트와의 네트워크 연결을 유지하므로 멀티스레드여야 합니다.
  • 라이브러리에 대한 일부 요청은 동기적으로 처리될 수 있지만 대부분은 시간이 너무 오래 걸리고 백그라운드에서 처리되어야 하며 성공(또는 실패)에 대해 클라이언트에 알립니다.

물론, 답변은 저의 구체적인 요구 사항을 다루거나, 더 많은 청중에게 중요한 일반적인 방법으로 질문에 대답하든 상관없이 환영합니다!

내 추측은, 지금까지.

지난 몇 달 동안 제가 수집한 몇 가지 가정과 결론은 다음과 같습니다.

  • 내부적으로 원하는 것은 무엇이든 사용할 수 있습니다. 예를 들어 C++은 연산자 오버로드, 다중 상속, 템플릿 메타 프로그래밍...그것을 다루는 휴대용 컴파일러가 있는 한 (gcc / g++을 생각해 보세요)
  • 그러나 내 인터페이스는 이름 망글링을 포함하지 않는 깨끗한 C 인터페이스여야 합니다.
  • 또한 내 인터페이스는 기본/기본 데이터 유형(및 포인터)이 매개 변수 및 반환 값으로 전달되는 함수로만 구성되어야 한다고 생각합니다.
  • 포인터를 사용하는 경우 참조된 메모리에서 직접 작동하지 않고 라이브러리로 다시 전달하는 데만 사용해야 한다고 생각합니다.
  • C++ 애플리케이션에서 사용하기 위해 객체 지향 인터페이스를 제공할 수도 있습니다(이 인터페이스는 이름을 망글링하기 쉬우므로 앱이 동일한 컴파일러를 사용하거나 소스 형태로 라이브러리를 포함해야 함).
  • 이것이 C#에서의 사용에도 해당됩니까?
  • Java SE/Java EE에서 사용하는 경우 Java Native Interface(JNI)가 적용됩니다.저는 그것에 대한 기본적인 지식을 가지고 있지만, 분명히 다시 한번 확인해야 합니다.
  • 모든 클라이언트 언어가 멀티스레딩을 잘 처리하는 것은 아니므로 클라이언트와 대화하는 단일 스레드가 있어야 합니다.
  • Java ME에서 사용하는 경우 JNI와 같은 것은 없지만 중첩된 VM으로 사용할 수 있습니다.
  • Bash 스크립트에서 사용하려면 명령줄 인터페이스가 있는 실행 파일이 있어야 함
  • 다른 클라이언트 언어에 대해서는 전혀 모르겠습니다.
  • 대부분의 클라이언트 언어의 경우 해당 언어로 작성된 일종의 어댑터 인터페이스가 있으면 좋습니다.Java 및 일부 다른 사용자를 위해 자동으로 생성할 수 있는 도구가 있다고 생각합니다.
  • 객체 지향 언어의 경우 라이브러리에 대한 인터페이스가 기능 기반이라는 사실을 숨기는 객체 지향 어댑터를 만드는 것이 가능할 수 있지만, 노력할 가치가 있는지 모르겠습니다.

가능한 하위 질문

  • 이것이 관리 가능한 노력으로 가능합니까, 아니면 단지 너무 많은 휴대성입니까?
  • 이런 종류의 디자인 기준에 대한 좋은 책/웹사이트가 있습니까?
  • 제 추측 중에 틀린 것이 있나요?
  • 어떤 오픈 소스 라이브러리가 그들의 디자인/인터페이스/소스로부터 배울 가치가 있습니까?
  • 메타: 이 질문은 꽤 긴데, 몇 개의 작은 질문으로 나눌 방법이 있나요?(이에 회신하는 경우 답변이 아닌 댓글로 해주세요)

대부분 맞습니다.직선적인 절차 인터페이스가 가장 좋습니다.(Cbtw(**)와 완전히 같지는 않지만 충분히 가깝습니다.)

저는 오픈 소스와 상업용 DLL을 많이 접합니다(*). 따라서 일상적인 작업에서 기억나는 몇 가지 사항은 기본적인 진실이 아니라 연구에 더 권장되는 영역입니다.

  • 특히 MS 컴파일러를 사용하는 경우 장식 및 유사한 "작은" 망글링 체계를 주의하십시오.가장 주목할 만한 것은 stdcall 규약이 때때로 VB를 위해 장식 생성으로 이어진다는 것입니다(장식은 함수 기호 이름 뒤에 @6과 같은 것입니다).
  • 모든 컴파일러가 실제로 모든 종류의 구조를 레이아웃할 수 있는 것은 아닙니다.
    • 그러니 노조를 남용하지 마세요.
    • 비트패킹 방지
    • 32비트 x86의 레코드를 패키징하는 것이 좋습니다.이론적으로는 더 느리지만, 적어도 모든 컴파일러가 패킹된 레코드에 빠르게 액세스할 수 있으며, 아키텍처가 발전함에 따라 공식적인 정렬 요구 사항은 시간이 지남에 따라 변화했습니다.
  • 윈도우즈에서는 stdcall을 사용합니다.Windows DLL의 기본값입니다.표준화되지 않은 빠른 호출 방지(특히 작은 레코드가 전달되는 방식)
  • 자동화된 머리글 번역을 쉽게 하기 위한 몇 가지 팁:
    • 매크로는 유형이 다르기 때문에 자동 변환하기 어렵습니다.사용하지 않고 기능을 사용합니다.
    • 각 포인터 유형에 대해 별도의 유형을 정의하고 함수 선언에 복합 유형(xtype **)을 사용하지 마십시오.
    • 가능한 한 "사용 전 정의" 만트라를 따르며, 이는 일반적으로 사용 전 정의가 필요한 언어인 경우 헤더를 변환하는 사용자가 헤더를 다시 정렬하는 것을 방지하고 원패스 파서가 쉽게 변환할 수 있도록 합니다.또는 자동 번역을 위해 컨텍스트 정보가 필요한 경우.
  • 필요 이상으로 노출하지 마십시오.가능한 경우 핸들 유형을 불투명하게 둡니다.나중에 버전 문제가 발생할 뿐입니다.
  • 레코드/구조체 또는 배열과 같은 구조화된 형식을 반환 형식의 함수로 반환하지 마십시오.
  • 항상 버전 확인 기능이 있습니다(구분을 위해 필요함).
  • 열거형과 부울형에 주의하십시오.다른 언어들은 약간 다른 가정을 가질 수 있습니다.사용할 수는 있지만 사용자의 동작 방식과 크기를 잘 문서화할 수 있습니다.또한 미리 생각하고, 몇 개의 필드를 추가해도 열거형이 커지지 않도록 하고, 인터페이스를 끊습니다.(예: Delphi/pascal의 경우 기본적으로 부울은 0 또는 1이며 다른 값은 정의되지 않습니다.C와 같은 불리언을 위한 특별한 유형이 있습니다(원래는 C 인터페이스가 아닌 COM을 위해 도입되었지만 바이트, 16비트 또는 32비트 워드 크기).
  • 저는 char + length 포인터를 별도의 필드로 사용하는 문자열 유형을 선호합니다(COM도 이렇게 합니다).가급적이면 종료된 제로에 의존하지 않아도 됩니다.이는 보안(오버플로) 이유뿐만 아니라 이러한 방식으로 Delli 네이티브 유형에 인터페이스하는 것이 더 쉽고 저렴하기 때문입니다.
  • 메모리는 항상 메모리 관리의 완전한 분리를 장려하는 방식으로 API를 생성합니다.IOW는 메모리 관리에 대해 아무것도 가정하지 않습니다.즉, lib의 모든 구조는 사용자의 메모리 관리자를 통해 할당되며, 함수가 구조체를 사용자에게 전달하는 경우 "클라이언트" 메모리 관리로 만들어진 포인터를 저장하는 대신 해당 구조체를 복사합니다.조만간 실수로 무료로 전화를 걸거나 재할당을 할 것이기 때문입니다 :-)
  • (인터페이스가 아닌 대화 언어), 보조 프로세서 예외 마스크 변경을 꺼립니다.일부 언어에서는 표준 부동 소수점 오류(예외) 처리를 준수하기 위해 이를 변경합니다.
  • 콜백은 항상 사용자가 구성할 수 있는 컨텍스트와 쌍으로 구성해야 합니다.글로벌 변수를 정의하지 않고 콜백 상태를 제공하는 데 사용할 수 있습니다.(예: 객체 인스턴스)
  • 보조 프로세서 상태 단어에 주의하십시오.다른 사용자에 의해 변경되어 코드가 손상될 수 있으며 변경하면 다른 코드가 작동하지 않을 수 있습니다.상태 단어는 일반적으로 호출 규칙의 일부로 저장/복원되지 않습니다.적어도 실제로는 그렇지 않습니다.
  • C 스타일 변수 매개 변수를 사용하지 않습니다.일부 언어에서는 Delphi 프로그래머가 많은 하드웨어를 인터페이스하여 공급업체 SDK 헤더를 변환하는 작업인 안전하지 않은 방법으로 매개 변수 수를 변경할 수 없습니다(*).밤에는 무료 파스칼 개발자가 윈도우 헤더를 담당합니다.

(**) 이는 "C"가 이진수를 의미하는 것이 사용된 C 컴파일러에 여전히 의존하기 때문입니다. 특히 실제 범용 시스템 ABI가 없는 경우에는 더욱 그렇습니다.다음과 같은 것을 생각해 보십시오.

  • 일부 이진 형식에 밑줄 접두사 추가(a.out, Coff?)
  • 때때로 다른 C 컴파일러들은 값에 의해 전달된 작은 구조들로 무엇을 해야 하는지에 대해 다른 의견을 가지고 있습니다.공식적으로 그들은 그것을 전혀 지원해서는 안 되지만, 대부분은 지원합니다.
  • 구조체 패킹은 호출 규약의 세부 사항과 마찬가지로 때때로 다릅니다(정수 레지스터를 건너뛰거나 매개 변수를 FPU 레지스터에 등록할 수 없는 경우 등).

자동 헤더 변환 ====

SWIG는 잘 모르지만, delphi 전용 헤더 도구(h2pas, Darth/headconv 등)를 알고 사용합니다.

하지만 저는 출력이 좋지 않은 경우가 더 많기 때문에 완전 자동 모드에서는 절대 사용하지 않습니다.주석이 줄을 바꾸거나 제거되고 형식이 유지되지 않습니다.

저는 보통 머리글을 위로 분할하는 작은 스크립트를 만듭니다(파스칼에서는 적절한 문자열 지원을 통해 무엇이든 사용할 수 있습니다). 그런 다음 비교적 동질적인 부분(예: 구조만 또는 정의만)에서 도구를 사용해 봅니다.

그런 다음 자동 변환 출력이 마음에 드는지 확인하고 사용하거나 특정 변환기를 직접 만들어 봅니다.서브셋을 위한 것이기 때문에 완전한 헤더 변환기를 만드는 것보다 훨씬 쉽습니다.물론 제 목표가 무엇인지에 따라 조금 다릅니다.(읽을 수 있는 머리글 또는 빠르고 지저분함).각 단계에서 저는 몇 가지 대체 작업을 수행할 수 있습니다(sed 또는 에디터 사용).

Winapi commctl 및 ActiveX/comctl 헤더에 대해 수행한 가장 복잡한 방식입니다.거기서 저는 IDL과 C 헤더(인터페이스의 IDL은 C에 있는 구문 분석 불가능한 매크로의 묶음이고 나머지는 C 헤더)를 결합하여 80% 정도의 매크로를 입력할 수 있었습니다(전송 메시지 매크로의 유형 캐스트를 매크로 선언으로 다시 전파함으로써). 합리적인 기본값(wparam, lparam, lresult)을 사용했습니다.

반자동화된 방법은 선언 순서가 다르다는 단점이 있습니다(예: 첫 번째 상수, 그 다음 구조, 그 다음 기능 선언). 이는 때때로 유지보수를 힘들게 합니다.따라서 저는 항상 비교하기 위해 원래 헤더/sdk를 보관합니다.

Jedi winapi 변환 프로젝트는 더 많은 정보를 가지고 있을지도 모릅니다, 그들은 윈도우 헤더의 약 절반을 델파이로 번역했고, 따라서 엄청난 경험을 가지고 있습니다.

잘은 모르겠지만 Windows용이라면 C와 같은 스트레이트 API(WINAPI와 유사)를 사용하거나 COM 구성 요소로 코드를 패키징할 수 있습니다. 프로그래밍 언어가 Windows API를 호출하거나 COM 개체를 사용할 수 있기 때문입니다.

자동 래퍼 생성과 관련하여 SWIG 사용을 고려합니다.Java의 경우 모든 JNI 작업을 수행합니다.또한 복잡한 OO-C++ 인터페이스를 올바르게 변환할 수 있습니다(네스트 클래스, 템플릿 남용 및 Marco van de Voort가 언급한 클래스 등의 몇 가지 기본 지침을 따르면).

C라고 생각해요, 다른 건 없어요.C는 가장 인기 있는 프로그래밍 언어 중 하나입니다.그것은 많은 다른 소프트웨어 플랫폼에서 널리 사용되며, C 컴파일러가 존재하지 않는 컴퓨터 아키텍처는 거의 없습니다.모든 인기 있는 고급 언어는 C에 대한 인터페이스를 제공합니다.따라서 존재하는 거의 모든 플랫폼에서 라이브러리에 액세스할 수 있습니다.객체 지향 인터페이스를 제공하는 것에 대해 너무 걱정하지 마십시오.C로 라이브러리를 작성한 후에는 적절한 클라이언트 언어로 OOP, 기능 또는 기타 스타일 인터페이스를 작성할 수 있습니다.C의 유연성과 가능성을 제공하는 다른 시스템 프로그래밍 언어는 없습니다.

MIPS 가상 시스템 메모리를 나타내는 int[][]에 대한 어레이 경계 검사 때문에 순수 Java보다 NestedVM이 느릴 것 같습니다.너무 좋은 개념이지만 지금 당장은 충분히 성능이 좋지 않을 수 있습니다(전화 제조업체가 중첩을 추가할 때까지).VM 지원(지원하는 경우!), 현재 대부분의 작업이 느려집니다. n'est-cepas)?JPEG는 오류 없이 압축을 풀 수 있지만 속도는 매우 중요합니다! :)

당신이 쓴 글에서 눈에 띄는 것은 아무것도 없습니다. 그렇다고 그것이 옳고 그른 것은 아닙니다!원칙(주로 정직하게 단어와 언어 선택을 듣는 것)은 대략적인 모범 사례처럼 들리지만, 저는 당신이 말한 모든 것에 대해 자세히 생각하지 않았습니다.당신이 스스로 말했듯이, 이것은 정말로 몇 가지 질문이 되어야 합니다.하지만 물론 이러한 작업을 수행하는 것이 자동으로 쉽지는 않습니다. 단지 마지막으로 작업한 코드 기반과 약간 다른 아키텍처에 고정되어 있기 때문입니다...! ;)

내 생각:

C 인터페이스 호환성에 대한 귀하의 의견은 모두 합리적인 것으로 들립니다. 메모리 관리 정책을 제대로 다루지 못하는 것으로 보이는 것을 제외하면 대부분의 모범 사례입니다. 일부 문장은 약간 모호하고 모호하며 잘못 들립니다.메모리 관리의 설계는 기능 자체보다는 응용프로그램에서 만들어진 액세스 패턴에 따라 크게 결정됩니다.표준 ANSIC API, 유닉스 API, Win32 API, 코코아, J2SE 등과 같은 휴대용 인터페이스를 만드는 다른 사람들의 시도를 주의 깊게 연구해 보는 것이 좋습니다.

저라면 일반 Java 및 Davidlik 가상 머신 Java의 공통 요소 중 신중하게 선택된 하위 집합으로 라이브러리를 작성하고 C를 지원하는 플랫폼을 위해 코드를 C로 변환하는 사용자 지정 파서를 작성할 것입니다. 물론 대부분이 해당됩니다.다양한 크기의 int, bool, Strings, Dictionary 및 Array 데이터 유형으로 제한하고 이러한 데이터 유형을 신중하게 사용하여 대부분의 시간 동안 성능에 영향을 미치지 않고 교차 플랫폼 문제에 도움이 될 것을 제안합니다.

당신의 가정은 괜찮아 보이지만, 저는 당신이 이미 당신의 가정에서 발견한 많은 문제를 볼 수 있습니다.당신이 말했듯이, 당신은 실제로 c++ 클래스와 메소드를 내보낼 수 없기 때문에, 당신은 함수 기반의 c 인터페이스를 제공해야 합니다.당신이 그것을 중심으로 어떤 외관을 짓든, 그것은 본질적으로 기능 기반의 인터페이스로 남을 것입니다.

제가 보는 기본적인 문제는 사람들이 특정 언어와 런타임을 선택한다는 것입니다. 왜냐하면 그들의 사고 방식(기능적 또는 객체 지향)이나 그들이 다루는 문제(웹 프로그래밍, 데이터베이스 등)가 어떤 식으로든 해당 언어와 일치하기 때문입니다.c에서 구현된 라이브러리는 자체적으로 프로그래밍하지 않는 한 아마도 익숙한 라이브러리처럼 느껴지지 않을 것입니다.개인적으로, 저는 파이썬을 사용할 때는 항상 "파이썬 느낌"이 나는 라이브러리를 선호하고, 자바 EE를 할 때는 c와 c++을 알지만 자바 EE처럼 느껴지는 라이브러리를 선호합니다.

따라서 여러분의 노력은 실제적으로 거의 쓸모가 없을 수도 있습니다. 왜냐하면 사람들은 아마도 사고방식을 고수하고 싶어할 것이고, 업무를 수행하지만 적합하지 않은 라이브러리를 사용하는 것보다 기능을 다시 구현하기를 원할 것이기 때문입니다.

저는 또한 원하는 휴대성이 개발을 심각하게 방해할 것을 우려합니다.필요한 무한 빌드 설정과 테스트를 생각해 보십시오.저는 5개의 운영 체제(모두 posix와 유사하지만 여전히)와 약 10개의 컴파일러에 대한 호환성을 유지하기 위해 노력한 프로젝트를 수행한 적이 있습니다. 빌드는 테스트하고 유지해야 하는 악몽이었습니다.

매개 변수 및 반환 값으로 전달되거나 명령줄 호출을 통해 파일로 전달되는 XML 인터페이스를 지정합니다.이것은 일반적인 함수 인터페이스처럼 직접적으로 보이지는 않지만 Java와 같은 실행 파일에 액세스하는 가장 실용적인 방법입니다.

언급URL : https://stackoverflow.com/questions/1932883/how-to-design-a-c-c-library-to-be-usable-in-many-client-languages