Programing

C ++ 17, C ++ 14 및 C ++ 11 개체를 연결하는 것이 안전합니까?

lottogame 2020. 11. 12. 07:40
반응형

C ++ 17, C ++ 14 및 C ++ 11 개체를 연결하는 것이 안전합니까?


모두 동일한 컴파일러 / 버전 에서 생성 된 세 개의 컴파일 된 객체가 있다고 가정합니다 .

  1. A는 C ++ 11 표준으로 컴파일되었습니다.
  2. B는 C ++ 14 표준으로 컴파일되었습니다.
  3. C는 C ++ 17 표준으로 컴파일되었습니다.

단순화를 위해 모든 헤더가 세 가지 표준 버전간에 의미가 변경되지 않은 구문 만 사용하여 C ++ 11로 작성되었다고 가정 해 보겠습니다. 따라서 상호 종속성이 헤더 포함으로 올바르게 표현되고 컴파일러가 객체를 처리하지 않았습니다.

이러한 객체의 어떤 조합이 단일 바이너리에 링크하는 것이 안전하지 않습니까? 왜?


편집 : 주요 컴파일러 (예 : gcc, clang, vs ++)를 다루는 답변을 환영합니다.


이러한 객체의 어떤 조합이 단일 바이너리에 링크하는 것이 안전하지 않습니까? 왜?

GCC의 경우 개체 A, B 및 C의 모든 조합을 함께 연결하는 것이 안전합니다. 모두 동일한 버전으로 빌드 된 경우 ABI와 호환되며 표준 버전 (예 : -std옵션)은 아무런 차이가 없습니다.

왜? 그것은 우리가 보장하기 위해 열심히 노력하는 우리 구현의 중요한 속성이기 때문입니다.

당신은 문제가있는 경우는 GCC의 다른 버전으로 컴파일 된 개체 함께 연결하는 경우입니다 그 표준 GCC의 지원이 완료되기 전에 새로운 C ++ 표준에서 불안정 기능을 사용하고 있습니다. 예를 들어 GCC 4.9를 사용하여 개체를 컴파일하고 -std=c++11GCC 5를 사용하여 다른 개체 를 컴파일하면 -std=c++11문제가 발생합니다. C ++ 11 지원은 GCC 4.x에서 실험적 이었으므로 GCC 4.9와 5 버전의 C ++ 11 기능간에 호환되지 않는 변경 사항이있었습니다. 마찬가지로 GCC 7로 하나의 객체를 컴파일 -std=c++17하고 GCC 8로 다른 객체 를 컴파일 -std=c++17하면 GCC 7 및 8에서 C ++ 17 지원이 아직 실험적이고 발전 중이기 때문에 문제가 발생합니다.

반면에 다음 개체의 모든 조합이 작동합니다 ( libstdc++.so버전 에 대한 아래 참고 참조 ).

  • GCC 4.9로 컴파일 된 객체 D 및 -std=c++03
  • GCC 5로 컴파일 된 객체 E 및 -std=c++11
  • GCC 7 및 -std=c++17

이는 사용 된 세 가지 컴파일러 버전 모두에서 C ++ 03 지원이 안정적이므로 모든 개체간에 C ++ 03 구성 요소가 호환되기 때문입니다. C ++ 11 지원은 GCC 5부터 안정적이지만 객체 D는 C ++ 11 기능을 사용하지 않으며 객체 E와 F는 모두 C ++ 11 지원이 안정적인 버전을 사용합니다. C ++ 17 지원은 사용 된 컴파일러 버전에서 안정적이지 않지만 개체 F 만 C ++ 17 기능을 사용하므로 다른 두 개체와의 호환성 문제가 없습니다 (공유하는 유일한 기능은 C ++ 03에서 제공됨). 또는 C ++ 11 및 사용 된 버전이 해당 부분을 확인합니다). 나중에 GCC 8을 사용하여 네 번째 객체 G -std=c++17를 컴파일하려는 경우 F와 G의 C ++ 17 기호가 호환되지 않기 때문에 동일한 버전 (또는 F에 연결하지 않음)으로 F를 다시 컴파일해야합니다.

위에서 설명한 D, E 및 F 간의 호환성에 대한 유일한주의 사항은 프로그램이 libstdc++.soGCC 7 (또는 이후 버전) 공유 라이브러리를 사용해야한다는 것 입니다. 객체 F는 GCC 7로 컴파일되었으므로 해당 릴리스의 공유 라이브러리를 사용해야합니다. GCC 7로 프로그램의 일부를 컴파일하면 libstdc++.soGCC 4.9 또는 GCC 5에 없는 심볼에 대한 종속성이 발생할 수 있기 때문입니다 . 마찬가지로, GCC 8로 빌드 된 객체 G에 연결 한 경우 GCC 8에서를 사용하여 libstdc++.soG에 필요한 모든 기호를 찾을 수 있도록해야합니다. 간단한 규칙은 프로그램이 런타임에 사용하는 공유 라이브러리가 최소한 객체를 컴파일하는 데 사용되는 버전만큼 새로운 것인지 확인하는 것입니다.

질문에 대한 의견에서 이미 언급했듯이 GCC를 사용할 때 또 다른주의 사항은 GCC 5 이후 libstdc ++에서 사용할 수있는 두 가지 구현이std::string 있다는 것입니다 . 두 구현은 링크와 호환되지 않지만 (다른 이름이 엉켜서 함께 연결될 수 없음) 동일한 바이너리에서 공존 할 수 있습니다 (다른 이름이 엉켜 있으므로 한 개체가 사용하는 경우 충돌하지 마십시오 std::string. 다른 용도 std::__cxx11::string). 객체가 사용하는 std::string경우 일반적으로 모두 동일한 문자열 구현으로 컴파일되어야합니다. -D_GLIBCXX_USE_CXX11_ABI=0원래 gcc4-compatible구현 -D_GLIBCXX_USE_CXX11_ABI=1을 선택하거나 새 cxx11구현 을 선택 하려면로 컴파일 하십시오 (이름에 속지 마십시오. C ++ 03에서도 사용할 수 있습니다.cxx11C ++ 11 요구 사항을 준수하기 때문에). 어떤 구현이 기본값인지는 GCC가 구성된 방법에 따라 다르지만 기본값은 매크로를 사용하여 컴파일 타임에 항상 재정의 할 수 있습니다.


답에는 두 부분이 있습니다. 컴파일러 수준에서의 호환성 및 링커 수준에서의 호환성. 전자부터 시작합시다.

모든 헤더가 C ++ 11로 작성되었다고 가정 해 보겠습니다.

동일한 컴파일러를 사용한다는 것은 타겟 C ++ 표준에 관계없이 동일한 표준 라이브러리 헤더 및 소스 파일 (컴파일러와 관련된 파일)이 사용된다는 것을 의미합니다. 따라서 표준 라이브러리의 헤더 파일은 컴파일러에서 지원하는 모든 C ++ 버전과 호환되도록 작성됩니다.

즉, 번역 단위를 컴파일하는 데 사용되는 컴파일러 옵션이 특정 C ++ 표준을 지정하는 경우 최신 표준에서만 사용할 수있는 모든 기능에 액세스 할 수 없습니다. 이것은 __cplusplus지시문을 사용하여 수행됩니다 . 사용 방법에 대한 흥미로운 예 벡터 소스 파일을 참조하십시오 . 마찬가지로 컴파일러는 최신 버전의 표준에서 제공하는 모든 구문 기능을 거부합니다.

그 모든 것은 당신의 가정이 당신이 작성한 헤더 파일에만 적용될 수 있음을 의미합니다. 이러한 헤더 파일은 다른 C ++ 표준을 대상으로하는 다른 번역 단위에 포함될 때 비 호환성을 유발할 수 있습니다. 이것은 C ++ 표준의 부록 C에서 논의됩니다. 4 개의 절이 있습니다. 첫 번째 절만 논의하고 나머지는 간단히 언급하겠습니다.

C.3.1 조항 2 : 어휘 규약

작은 따옴표는 C ++ 11에서 문자 리터럴을 구분하는 반면 C ++ 14 및 C ++ 17에서는 숫자 구분 기호입니다. 순수 C ++ 11 헤더 파일 중 하나에 다음 매크로 정의가 있다고 가정합니다.

#define M(x, ...) __VA_ARGS__

// Maybe defined as a field in a template or a type.
int x[2] = { M(1'2,3'4) };

헤더 파일을 포함하지만 각각 C ++ 11 및 C ++ 14를 대상으로하는 두 개의 변환 단위를 고려하십시오. C ++ 11을 대상으로 할 때 따옴표 안의 쉼표는 매개 변수 구분 기호로 간주되지 않습니다. 매개 변수는 한 번만 있습니다. 따라서 코드는 다음과 같습니다.

int x[2] = { 0 }; // C++11

반면에 C ++ 14를 대상으로하는 경우 작은 따옴표는 숫자 구분 기호로 해석됩니다. 따라서 코드는 다음과 같습니다.

int x[2] = { 34, 0 }; // C++14 and C++17

The point here is that using single quotes in one of the pure C++11 header files can result in surprising bugs in the translation units that target C++14/17. Therefore, even if a header file is written in C++11, it has to be written carefully to ensure that it's compatible with later versions of the standard. The __cplusplus directive may be useful here.

The other three clauses from the standard include:

C.3.2 Clause 3: basic concepts

Change: New usual (non-placement) deallocator

Rationale: Required for sized deallocation.

Effect on original feature: Valid C++2011 code could declare a global placement allocation function and deallocation function as follows:

void operator new(std::size_t, std::size_t); 
void operator delete(void*, std::size_t) noexcept;

In this International Standard, however, the declaration of operator delete might match a predefined usual (non-placement) operator delete (3.7.4). If so, the program is ill-formed, as it was for class member allocation functions and deallocation functions (5.3.4).

C.3.3 Clause 7: declarations

Change: constexpr non-static member functions are not implicitly const member functions.

Rationale: Necessary to allow constexpr member functions to mutate the object.

Effect on original feature: Valid C++2011 code may fail to compile in this International Standard.

For example, the following code is valid in C++2011 but invalid in this International Standard because it declares the same member function twice with different return types:

struct S {
constexpr const int &f();
int &f();
};

C.3.4 Clause 27: input/output library

Change: gets is not defined.

Rationale: Use of gets is considered dangerous.

Effect on original feature: Valid C++2011 code that uses the gets function may fail to compile in this International Standard.

Potential incompatibilities between C++14 and C++17 are discussed in C.4. Since all the non-standard header files are written in C++11 (as specified in the question), these issues will not occur, so I will not mention them here.

Now I'll discuss compatibility at the linker level. In general, potential reasons for incompatibilities include the following:

  • The format of the object files.
  • Program startup and termination routines and the main entry point.
  • Whole program optimization (WPO).

If the format of the resulting object file depends on the target C++ standard, the linker must be able to link the different object files. In GCC, LLVM, and VC++, this is fortunately not the case. That is, the format of objects files is the same irrespective of the target standard, although it is highly dependent on the compiler itself. In fact, none of the linkers of GCC, LLVM, and VC++ require knowledge about the target C++ standard. This also means that we can link object files that are already compiled (statically linking the runtime).

If the program startup routine (the function that calls main) is different for different C++ standards and the different routines are not compatible with each other, then it would not be possible to link the object files. In GCC, LLVM, and VC++, this is fortunately not the case. In addition, the signature of the main function (and the restrictions that apply on it, see Section 3.6 of the standard) is the same in all C++ standards, so it doesn't matter in which translation unit it exists.

In general, WPO may not work well with object files compiled using different C++ standards. This depends on exactly which stages of the compiler require knowledge of the target standard and which stages don't and the impact that it has on inter-procedural optimizations that cross object files. Fortunately, GCC, LLVM, and VC++ are well designed and don't have this issue (not that I'm aware of).

Therefore, GCC, LLVM, and VC++ have been designed to enable binary compatibility across different versions of the C++ standard. This is not really a requirement of the standard itself though.

By the way, although the VC++ compiler offers the std switch, which enables you to target a particular version of the C++ standard, it does not support targeting C++11. The minimum version that can be specified is C++14, which is the default starting from Visual C++ 2013 Update 3. You could use an older version of VC++ to target C++11, but then you would have to use different VC++ compilers to compile different translation units that target different versions of the C++ standard, which would at the very least break WPO.

CAVEAT: My answer may not be complete or very precise.


New C++ standards are come in two parts: language features and standard library components.

As you mean by new standard, changes in language itself (e.g. ranged-for) there's almost no problem (sometimes conflicts are exists in 3rd party library headers with newer standard language features).

But standard library...

Each compiler version comes with an implementation of C++ standard library (libstdc++ with gcc, libc++ with clang, MS C++ standard library with VC++,...) and exactly one implementaion, not many implementation for each standard version. Also in some cases you may use other implementation of standard library than compiler provided. What you should care is linking an older standard library implementation with a newer one.

The conflict that could occur between 3rd party libraries and your code is the standard library (and other libraries) that links to that 3rd party libraries.

참고URL : https://stackoverflow.com/questions/46746878/is-it-safe-to-link-c17-c14-and-c11-objects

반응형