C ++ : 이진 수준에서의 표준화 부족


14

ISO / ANSI가 이진 수준에서 C ++을 표준화하지 않은 이유는 무엇입니까? C ++에는 많은 이식성 문제가 있는데, 이는 바이너리 수준에서의 표준화가 없기 때문입니다.

돈 상자 (그의 책에서 인용, 기록 필수 COM , 장 COM으로 더 나은 C ++ )

C ++ 및 이식성


C ++ 클래스를 DLL로 배포하기로 결정하면 C ++ 의 근본적인 약점 중 하나 , 즉 이진 수준에서의 표준화 부족에 직면하게 됩니다 . ISO / ANSI C ++ Draft Working Paper는 컴파일 할 프로그램과 그 실행의 의미 효과를 체계화하려고 시도하지만 C ++의 이진 런타임 모델을 표준화하지는 않습니다. 클라이언트가 FastString DLL 을 빌드하는 데 사용 된 환경이 아닌 C ++ 개발 환경에서 FastString DLL의 가져 오기 라이브러리에 연결하려고 할 때이 문제가 처음으로 명백해 집니다.

이진 표준화의 부족으로 인해 더 많은 이점이 있습니까?


이것은 programmers.stackexchange.com 에서 더 주관적인 질문 인 방법으로 보았습니까?
Stephen Furlani

1
실제로 내 관련 질문 : stackoverflow.com/questions/2083060/…
AraK

4
돈 박스는 열성입니다. 그를 무시해.
John Dibling

8
C는 바이너리 수준에서도 ANSI / ISO에 의해 표준화되지 않았다. OTOH C는이 사실상의 표준 ABI보다는 합법적 하나. C ++에는 표준화 된 ABI가 없습니다. 제조업체마다 구현 목표가 다르기 때문입니다. 예를 들어, Windows SEH 위에 VC ++ 피기 백 예외가 있습니다. POSIX에는 SEH가 없으므로 해당 모델을 사용하는 것이 의미가 없습니다 (G ++ 및 MinGW는 해당 모델을 사용하지 않음).
Billy ONeal

3
나는 이것을 약점이 아닌 기능으로 본다. 구현을 특정 ABI에 바인딩하면 혁신이 없으며 새로운 하드웨어가 언어 설계에 바인딩됩니다 (하드웨어 업계에서 오랜 시간이 지난 각 새 버전 사이에 15 년이 있기 때문에). 코드를보다 효율적으로 실행하기위한 새로운 아이디어를 혁신하지는 않을 것입니다. 가격은 실행 파일의 모든 코드가 동일한 컴파일러 / 버전 (문제는 아니지만 큰 코드)으로 작성되어야한다는 것입니다.

답변:


16

이진 호환 컴파일 형식의 언어는 비교적 새로운 단계 (*)입니다 (예 : JVM 및 .NET 런타임). C 및 C ++ 컴파일러는 일반적으로 기본 코드를 생성합니다.

장점은 JIT, 바이트 코드 인터프리터, VM 또는 기타 다른 것들이 필요 없다는 것입니다. 예를 들어, 기계가 기본적으로 Java 바이트 코드를 실행할 수 있거나 Java에서 비 이진 호환 원시로 변환 할 수있는 경우가 아니면 기계 시작시 실행되는 부트 스트랩 코드를 멋진 휴대용 Java 바이트 코드로 작성할 수 없습니다. 실행 코드 (이론상 : 실제로 부트 스트랩 코드에 권장 될 수 있는지 확실하지 않음) 소스 수준에서도 휴대용 C ++은 아니지만 C ++로 작성할 수 있습니다. 마법 하드웨어 주소로 많은 혼란을 겪기 때문입니다.

단점은 물론 네이티브 코드는 컴파일 된 아키텍처에서 전혀 실행되지 않으며 실행 파일은 실행 파일 형식을 이해하는 로더 만로드 할 수 있으며 동일한 아키텍처 ABI.

그 정도까지 도달하더라도 두 실행 파일을 함께 연결하면 실제로 다음과 같은 경우에만 실제로 올바르게 작동합니다. (a) 하나의 정의 규칙을 위반하지 않습니다. 같은 클래스에 대해 서로 다른 정의를 사용하도록 (헤더에서 또는 서로 다른 구현에 정적으로 연결되어 있기 때문에); (b) 구조 레이아웃과 같은 모든 관련 구현 세부 사항은 각각 컴파일 될 때 유효한 컴파일러 옵션에 따라 동일합니다.

C ++ 표준에서이 모든 것을 정의하려면 현재 구현자가 사용할 수있는 많은 자유를 제거해야합니다. 구현자는 특히 C ++ (그리고 같은 문제가있는 C)로 매우 낮은 수준의 코드를 작성할 때 이러한 자유를 사용 합니다.

바이너리로 이식 가능한 대상의 경우 C ++과 비슷한 것을 작성하려면 .NET을 대상으로하는 C ++ / CLI와 Windows가 아닌 다른 곳에서 .NET을 실행할 수 있도록 Mono가 있습니다. 모노에서 실행될 순수한 CIL 어셈블리를 생성하도록 MS의 컴파일러를 설득하는 것이 가능하다고 생각합니다.

이진 이식 가능 C 또는 C ++ 환경을 만들기 위해 LLVM과 같이 수행 할 수있는 작업도 있습니다. 그럼에도 불구하고 널리 퍼진 예가 나온다는 것을 모른다.

그러나 이것들은 모두 C ++이 구현에 따라 달라지는 많은 유형 (예 : 유형의 크기)을 수정하는 데 의존합니다. 그런 다음 이식 가능한 바이너리를 이해하는 환경은 코드를 실행할 시스템에서 사용할 수 있어야합니다. 이식이 불가능한 바이너리를 허용함으로써 C와 C ++는 이식 가능한 바이너리가 할 수없는 곳으로 갈 수 있으므로 표준에서 바이너리에 대해 아무 말도하지 않습니다.

그런 다음 특정 플랫폼에 구현은 일반적으로 아직 표준이 그들을 중지되지는 않지만, 옵션의 다른 세트 사이의 바이너리 호환성을 제공하지 않습니다. Don Box가 Microsoft의 컴파일러가 컴파일러 옵션에 따라 동일한 소스에서 호환되지 않는 바이너리를 생성하는 것을 좋아하지 않는다면, 불만을 제기 해야하는 컴파일러 팀입니다. C ++ 언어는 컴파일러 또는 OS가 필요한 모든 세부 정보를 찾아내는 것을 금지 하지 않으므로 Windows로 제한하면 C ++의 근본적인 문제는 아닙니다. Microsoft는 그렇게하지 않기로 결정했습니다 .

차이점은 종종 틀릴 수 있고 프로그램을 중단시킬 수있는 또 하나의 일로 나타납니다. 그러나 호환되지 않는 디버그 버전과 dll의 릴리스 버전 간에는 효율성이 크게 향상 될 수 있습니다.

[*] 아이디어가 처음 발명되었을 때, 아마도 1642 년이 될지 확신 할 수 없지만, C ++이 이진 이식성을 정의하지 못하게하는 디자인 결정에 전념 한 시간과 비교할 때 현재 인기는 비교적 새로운 것입니다.


@Steve 그러나 C에는 i386 및 AMD64에 ABI가 잘 정의되어 있으므로 GCC 버전 X로 컴파일 된 함수에 대한 포인터를 MSVC 버전 Y로 컴파일 된 함수에 전달할 수 있습니다. C ++ 함수로는 불가능합니다.
user877329 2016 년

7

크로스 플랫폼 및 크로스 컴파일러 호환성은 C 및 C ++의 주요 목표가 아닙니다. 그것들은 시대에 태어 났으며 플랫폼 별 및 컴파일러 별 시간 및 공간 최소화가 중요한 목적을 위해 고안되었습니다.

Stroustrup의 "C ++의 디자인과 진화"에서 :

"명시적인 목표는 런타임, 코드 압축 및 데이터 압축 측면에서 C를 일치시키는 것이 었습니다. ... 달성 된 이상은 클래스가있는 C를 사용할 수있는 모든 C에 사용할 수 있다는 것입니다."


1
+1-정확히. ARM과 Intel 박스 모두에서 작동하는 표준 ABI를 어떻게 구축 할 수 있습니까? 말이 안 돼요!
Billy ONeal

1
불행히도, 이것에 실패했습니다. 런타임에 C ++ 모듈을 동적으로로드하는 것을 제외하고 C가하는 모든 작업을 수행 할 수 있습니다. 노출 된 인터페이스에서 C 함수를 사용하도록 '복귀'해야합니다.
gbjbaanb

6

버그가 아니라 기능입니다! 이를 통해 구현자는 이진 레벨에서 구현을 최적화 할 수 있습니다. 리틀 엔디안 i386과 그 후손은 존재하거나 존재하는 유일한 CPU가 아닙니다.


6

인용에서 설명하는 문제는 "생각 심볼 이름 맹 글링 체계의 표준화 (의 매우 의도적 인 회피로 인해 발생 바이너리 수준에서 표준화 문제가 컴파일러의 관련이 있지만,"이 점에 오해의 소지가 문구 응용 프로그램 바이너리 인터페이스 ( ABI).

C ++는 함수 또는 데이터 객체의 서명 및 유형 정보와 클래스 / 네임 스페이스 멤버쉽을 symbol-name으로 인코딩하며, 다른 컴파일러는 다른 스키마를 사용할 수 있습니다. 결과적으로 정적 라이브러리, DLL 또는 객체 파일의 심볼은 다른 컴파일러 (또는 동일한 컴파일러의 다른 버전)를 사용하여 컴파일 된 코드와 연결되지 않습니다.

이 문제는 다른 컴파일러가 사용하는 체계의 예와 함께 여기 보다 더 잘 설명되고 설명되어 있습니다 .

고의로 표준화가 결여 된 이유도 여기에 설명되어 있습니다 .


3

ISO / ANSI의 목표는 C ++ 언어를 표준화하는 것이 었습니다. 문제 는 언어 표준 및 컴파일러 지원을 업데이트하는 데 몇 년 이 걸리기에 충분히 복잡해 보입니다 .

바이너리가 다른 CPU의 아키텍처와 다른 OS 환경에서 실행되어야하기 때문에 바이너리 호환성은 훨씬 더 복잡합니다.


그러나 인용문에 설명 된 문제는 실제로 "이진 수준 호환성"(저자의 용어 사용에도 불구하고)과는 아무런 관련이 없으며 "응용 프로그램 이진 인터페이스"라는 용어로 정의됩니다. 실제로 호환되지 않는 이름 맹 글링 문제에 대해 설명하고 있습니다.

@Clifford : name mangling scheme은 이진 수준 호환성의 하위 집합입니다. 후자는 우산 용어와 비슷합니다!
Nawaz

Windows 컴퓨터에서 Linux 바이너리를 실행하는 데 문제가 있는지 의심합니다. 적어도 스크립트 언어가 동일한 플랫폼에서 바이너리를 동적으로로드하고 실행할 수 있거나 앱이 다른 컴파일러로 작성된 구성 요소를 사용할 수 있기 때문에 플랫폼 당 ABI가 있다면 상황이 훨씬 나아질 것입니다. 오늘날 Linux에서는 C dll을 사용할 수 없으며 아무도 불평하지 않지만 C dll은 여전히 ​​python 앱에 의해로드 될 수 있습니다.이 앱은 이점이 발생합니다.
gbjbaanb

2

Andy는 크로스 플랫폼 호환성은 큰 목표는 아니지만 광범위한 플랫폼 및 하드웨어 구현은 목표였으며 결과적으로 매우 다양한 시스템에 적합한 구현을 작성할 수있었습니다. 이진 표준화는 이것을 실제로 달성 할 수 없게 만들었을 것입니다.

C 호환성도 중요했고 이것을 상당히 복잡하게 만들었습니다.

이후 일부 구현에 대해 ABI표준화 하려는 노력이있었습니다 .


Darn, 나는 C 호환성을 잊었다. 좋은 지적, +1!
Andy Thomas

1

C ++에 대한 표준이 없다는 것은 오늘날 분리 된 모듈 식 프로그래밍 세계에서 문제가된다고 생각합니다. 그러나 우리는 그러한 표준에서 원하는 것을 정의해야합니다.

어느 누구도 이진에 대한 구현 또는 플랫폼을 정의하고 싶어하지 않습니다. 따라서 x86 Windows dll을 가져 와서 x86_64 Linux 플랫폼에서 사용할 수 없습니다. 조금 많을 것입니다.

그러나 사람들이 원하는 것은 바이너리 수준의 표준화 된 인터페이스 인 C 모듈과 마찬가지로 C 모듈과 동일합니다. 현재 모듈 식 앱에 dll을로드하려면 C 함수를 내보내고 런타임에 바인딩합니다. C ++ 모듈로는 그렇게 할 수 없습니다. 가능하면 좋을 것입니다. 이는 하나의 컴파일러로 작성된 dll이 다른 컴파일러에 의해로드 될 수 있음을 의미합니다. 물론, 여전히 호환되지 않는 플랫폼 용으로 구축 된 dll을로드 할 수는 없지만 수정이 필요한 문제는 아닙니다.

따라서 표준 본문에서 모듈이 노출하는 인터페이스를 정의한 경우 C ++ 모듈을로드 할 때 유연성이 훨씬 높아지고 C ++ 코드를 C 코드로 노출하지 않아도되며 더 많이 사용할 수 있습니다. 스크립트 언어로 된 C ++

또한이 문제에 대한 해결책을 제공하려는 COM과 같은 문제를 겪을 필요도 없습니다.


1
+1. 예, 동의합니다. 다른 답변은 기본적으로 이진 표준화로 인해 아키텍처 별 최적화가 금지된다고 말함으로써 문제를 해결합니다. 그러나 그것은 요점이 아닙니다. 크로스 플랫폼 바이너리 실행 파일 형식을 주장하는 사람은 없습니다. 문제는 C ++ 모듈을 동적으로로드하는 표준 인터페이스 가 없다는 것 입니다.
Charles Salvia

1

C ++에는 많은 이식성 문제가 있는데, 이는 바이너리 수준에서의 표준화가 없기 때문입니다.

나는 그것이 그렇게 간단하다고 생각하지 않습니다. 제공된 답변은 이미 표준화에 중점을 두지 않은 것에 대한 훌륭한 근거를 제공하지만 C ++은 ABI 표준으로서 C와 진정으로 경쟁하기에 너무 언어가 풍부 할 수 있습니다 .

함수 오버로딩, vtable 비 호환성, 예외가있는 비 호환성 등으로 인해 이름이 바뀌면서 모듈 경계를 넘을 수 있습니다.이 모두가 정말 고통스럽고 적어도 vtable 레이아웃을 표준화 할 수 있기를 바랍니다.

그러나 ABI 표준은 단지 하나의 컴파일러에서 생성 된 C ++ dylib를 다른 컴파일러가 빌드 한 다른 바이너리에서 사용할 수 있도록 만드는 것이 아닙니다. ABI가 사용됩니다 언어 간 됩니다 . 적어도 첫 번째 부분을 다룰 수 있다면 좋을 것입니다.하지만 C ++이 보편적 인 ABI 수준에서 C와 경쟁하는 것을 볼 수있는 방법은 없습니다. 가장 광범위하게 호환되는 dylib를 만드는 데 중요합니다.

다음과 같이 내 보낸 간단한 함수 쌍을 상상해보십시오.

void f(Foo foo);
void f(Bar bar, int val);

... 그리고 상상 FooBar매개 변수화 된 생성자와 클래스했다, 생성자, 이동 생성자, 비 사소한 소멸자를 복사합니다.

그런 다음 Python / Lua / C # / Java / Haskell / etc 시나리오를 사용하십시오. 개발자가이 모듈을 가져 와서 해당 언어로 사용하려고합니다.

먼저 함수 오버로딩을 사용하여 심볼을 내보내는 방법에 대한 이름 관리 표준이 필요합니다. 이것은 더 쉬운 부분입니다. 그러나 실제로 "mangling"이라는 이름이되어서는 안됩니다. dylib 사용자는 이름을 기준으로 기호를 찾아야하므로 여기에 과부하가 발생하면 이름이 완전히 엉망처럼 보이지 않아야합니다. 어쩌면 기호 이름이 "f_Foo" "f_Bar_int"그런 종류 일 수도 있습니다 . 개발자가 실제로 정의한 이름과 충돌 할 수 없도록해야합니다. 아마도 ABI 사용을 위해 일부 기호 / 문자 / 수집을 예약했을 것입니다.

그러나 지금은 더 힘든 시나리오입니다. 예를 들어 Python 개발자는 이동 생성자, 복사 생성자 및 소멸자를 어떻게 호출합니까? 아마도 우리는 dylib의 일부로 그것들을 내보낼 수 있습니다. 하지만 경우 FooBar 다른 모듈에 수출하고 있습니다? 이 dylib와 관련된 기호와 구현을 복제해야합니까? 여러 가지 dylib 인터페이스에 얽혀서 여기에 객체를 만들고 여기에 복사하고, 여기에 복사하고, 여기에서 파괴해야하기 때문에 정말 성가 시게 될 수 있기 때문에 제안합니다. 동일한 기본 관심사가 C에 다소 적용될 수 있지만 (보다 수동 / 명시 적으로) C는 사람들이 프로그래밍하는 방식에 따라이를 피하는 경향이 있습니다.

이것은 어색함의 작은 샘플 일뿐입니다. f위 의 함수 중 하나가 BazExceptionJavaScript에 생성자 및 소멸자를 포함하고 std :: exception을 파생시키는 C ++ 클래스를 throw 하면 어떻게됩니까 ?

기껏해야 하나의 C ++ 컴파일러가 생성 한 바이너리에서 다른 바이너리가 생성하는 다른 바이너리로 작동하는 ABI 만 표준화 할 수 있다고 생각합니다. 물론 그것은 좋을 것이지만, 나는 이것을 지적하고 싶었습니다. 일반적으로 크로스 컴파일러에서 작동하는 일반화 된 라이브러리를 배포하기 위해 이러한 우려가 수반되는 경우가 많으며이를 실제로 일반화하고 호환되는 크로스 언어로 만들고자하는 경우가 많습니다.

제안 된 해결책

COM 스타일 인터페이스로 API / ABI에 C ++ 인터페이스를 사용하는 방법을 찾기 위해 고군분투 한 후에 제안한 솔루션은 "C / C ++"(pun) 개발자가되는 것입니다.

C를 사용하여 구현을 위해 C ++로 범용 ABI를 작성하십시오. 우리는 내보내기 함수와 같은 작업을 수행하여 힙에서 이러한 객체를 생성하고 파괴하는 명시 적 함수로 불투명 한 C ++ 클래스에 대한 포인터를 반환 할 수 있습니다. 구현에 C ++을 완전히 사용하더라도 ABI 관점에서 C 미학을 사랑하십시오. 함수 포인터 테이블을 사용하여 추상 인터페이스를 모델링 할 수 있습니다. 이 내용을 C API로 마무리하는 것은 지루하지만, 그와 함께 제공되는 배포의 이점과 호환성은 매우 가치가 있습니다.

그런 다음이 인터페이스를 너무 직접 사용하는 것을 좋아하지 않는다면 (적어도 RAII 이유로는 안 됨) SDK와 함께 제공되는 정적으로 연결된 C ++ 라이브러리에 원하는 모든 것을 랩핑 할 수 있습니다. C ++ 클라이언트가이를 사용할 수 있습니다.

파이썬 클라이언트는이 피 토닉을 만들 방법이 없기 때문에 C 또는 C ++ 인터페이스를 직접 사용하고 싶지 않습니다. 그들은 그것을 자체의 pythonique 인터페이스로 마무리하고 싶기 때문에 실제로 가능한 한 쉽게 만들기 위해 최소한의 C API / ABI를 내보내는 것이 좋습니다.

COM 스타일 인터페이스 등을 완고하게 제공하려는 것보다 많은 C ++ 산업이 더 많은 이점을 얻을 수 있다고 생각합니다. 또한이 딜 리브 사용자가 어색한 ABI에 대해 걱정할 필요가 없기 때문에 우리의 모든 삶을 더 쉽게 만들 것입니다. C는 간단하고 ABI 관점에서 간단하기 때문에 모든 종류의 FFI에 대해 자연스럽고 최소한으로 작동하는 API / ABI를 만들 수 있습니다.


1
"구현을 위해 C ++로 C를 사용하여 이러한 범용 ABI를 작성하십시오." ... 다른 많은 사람들처럼 똑같이합니다!
Nawaz

-1

왜 바이너리 수준에서 표준화하지 않는지 모르겠습니다. 그러나 나는 그것에 대해 내가하는 일을 알고 있습니다. Windows에서 함수 extern "C"BOOL WINAPI를 선언합니다. (물론 BOOL을 함수의 유형으로 바꾸십시오.) 그리고 그것들은 깨끗하게 내보내집니다.


2
그러나 만약 당신이 그것을 선언한다면 extern "C", 그것은 어떤 종류의위원회에 의해서도 부과되지 않더라도 일반적인 PC 하드웨어에 대한 사실상의 표준 인 C ABI를 사용할 것입니다 .
Billy ONeal

-3

unzip foo.zip && make foo.exe && foo.exe소스의 이식성을 원하는 경우에 사용하십시오 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.