Language linkage
서로 다른 프로그래밍 언어로 작성된 프로그램 단위 간의 연결을 제공합니다.
|
이는 선언을 해당 모듈에서 분리하는 데에도 사용할 수 있습니다. 자세한 내용은 Module ownership 을 참조하십시오. |
(since C++20) |
extern
string-literal
{
declaration-seq
(선택적)
}
|
(1) | ||||||||
extern
string-literal
declaration
|
(2) | ||||||||
| string-literal | - | 필요한 언어 연결을 지정하는 평가되지 않는 문자열 리터럴 |
| declaration-seq | - | 중첩된 연결 지정을 포함할 수 있는 선언 시퀀스 |
| declaration | - | 선언 |
목차 |
설명
모든 함수 타입, 모든 외부 링크 를 가진 함수 이름, 그리고 모든 외부 링크 를 가진 변수 이름은 언어 링크 라는 속성을 가집니다. 언어 링크는 다른 프로그래밍 언어로 작성된 프로그램 단위와 링크하기 위해 필요한 요구 사항들의 집합을 캡슐화합니다: 호출 규약 , 이름 맹글링 (이름 데코레이션) 알고리즘 등.
오직 두 가지 언어 연결만 지원이 보장됩니다:
- "C++" , 기본 언어 연결.
- "C" , C 프로그래밍 언어로 작성된 함수와 연결할 수 있게 하며, C++ 프로그램에서 C로 작성된 유닛에서 호출될 수 있는 함수를 정의할 수 있게 합니다.
extern "C" { int open(const char *path_name, int flags); // C 함수 선언 } int main() { int fd = open("test.txt", 0); // C++ 프로그램에서 C 함수 호출 } // 이 C++ 함수는 C 코드에서 호출될 수 있음 extern "C" void handler(int) { std::cout << "Callback invoked\n"; // C++ 기능 사용 가능 }
언어 연결은 모든 함수 타입의 일부이므로, 함수 포인터도 언어 연결을 유지합니다. 함수 타입의 언어 연결(호출 규약을 나타냄)과 함수 이름의 언어 연결(네임 맹글링을 나타냄)은 서로 독립적입니다:
extern "C" void f1(void(*pf)()); // C 링크를 가지는 함수 f1을 선언합니다. // 이 함수는 void를 반환하고, 매개변수를 취하지 않고 void를 반환하는 // C 함수에 대한 포인터를 인자로 받습니다 extern "C" typedef void FUNC(); // 매개변수를 취하지 않고 void를 반환하는 C 함수 타입으로 // FUNC를 선언합니다 FUNC f2; // 이름 f2는 C++ 링크를 가지지만, 그 타입은 C 함수입니다 extern "C" FUNC f3; // 이름 f3는 C 링크를 가지며 그 타입은 C 함수 void()입니다 void (*pf2)(FUNC*); // 이름 pf2는 C++ 링크를 가지며, 그 타입은 // "void를 반환하고 '매개변수를 취하지 않고 void를 반환하는 C 함수에 대한 // 포인터' 타입의 인자를 하나 취하는 C++ 함수에 대한 포인터"입니다 extern "C" { static void f4(); // 함수 f4의 이름은 내부 링크를 가집니다 (언어 링크 없음) // 하지만 함수의 타입은 C 언어 링크를 가집니다 }
만약 동일한 엔티티의 두 선언 이 서로 다른 언어 링크를 부여한다면, 프로그램은 ill-formed입니다; 두 선언 중 어느 것도 다른 선언에서 도달할 수 없는 경우 진단은 요구되지 않습니다. 링크 지정 없이 엔티티를 재선언하는 경우 해당 엔티티와 그 타입(존재하는 경우)의 언어 링크를 상속받습니다.
extern "C" int f(); extern "C++" int f(); // 오류: 다른 언어 링크 extern "C" int g(); int g(); // OK, C 언어 링크를 가짐 int h(); // 기본적으로 C++ 언어 링크를 가짐 extern "C" int h(); // 오류: 다른 언어 링크
"C" 링크에 대한 특별 규칙
클래스 멤버 , 후행 requires 절을 가진 friend 함수 , (C++20부터) 또는 비정적 멤버 함수가 "C" 언어 블록에 나타날 때, 해당 타입들의 링크지는 "C++" 로 유지됩니다(하지만 매개변수 타입이 있는 경우 해당 타입은 "C" 로 유지됨):
extern "C" { class X { void mf(); // 함수 mf와 그 타입은 C++ 언어 링크를 가짐 void mf2(void(*)()); // 함수 mf2는 C++ 언어 링크를 가짐; // 매개변수는 "C 함수에 대한 포인터" 타입을 가짐 }; } template<typename T> struct A { struct B; }; extern "C" { template<typename T> struct A<T>::B { friend void f(B*) requires true {} // C 언어 링크는 무시됨 }; } namespace Q { extern "C" void f(); // 잘못된 형식이 아님 }
C
가
"C"
언어 링크age를 가진 함수나 변수를 선언하는 선언이라고 가정합니다. 다른 선언
D
가 동일한 이름을 가진 엔티티를 선언하고 다음 조건 중 하나를 충족하는 경우,
C
와
D
는 동일한 엔티티를 선언합니다:
-
D는 전역 범위에 속하는 변수를 선언합니다. -
만약
C가 변수를 선언하면,D도 변수를 선언합니다. -
만약
C가 함수를 선언하면,D도 함수를 선언합니다.
일반적인 재선언
과 달리,
C
와
D
는 서로 다른
대상 범위
를 가질 수 있습니다:
extern "C" { int x; int f(); int g() { return 1; } } namespace A { int x; // 오류: "x" 재정의 int f(); // OK, "f" 재선언 int g() { return 1; } // 오류: "g" 재정의 }
그러나 이러한 선언의 제한 사항 은 여전히 적용되며, 이는 둘 다 함수를 선언하거나 둘 다 변수를 선언해야 하며, 선언된 엔티티들은 동일한 타입을 가져야 함을 의미합니다:
namespace A { extern "C" int x(); extern "C" int y(); } int x; // 오류: "x"를 다른 종류의 엔티티로 재선언함 namespace B { void y(); // 오류: 다른 타입으로 "y"를 재선언함 }
참고 사항
언어 명세는 오직 namespace scope 에서만 나타날 수 있습니다.
언어 명세의 중괄호는 스코프를 생성하지 않습니다.
언어 사양이 중첩될 경우, 가장 안쪽의 사양이 유효하게 적용됩니다.
언어 연결 명세 내에 직접 포함된 선언은 선언된 이름의 extern 지정자 를 포함하는 것처럼 처리되며, 이를 통해 링크 여부와 정의 인지의 여부가 결정됩니다.
extern "C" int x; // 선언이지 정의가 아님 // 위 줄은 extern "C" { extern int x; }와 동등함 extern "C" { int x; } // 선언이면서 정의 extern "C" double f(); static double f(); // 오류: 링크age 충돌 extern "C" static void g(); // 오류: 링크age 충돌
extern "C" 는 C++ 프로그램에서 C 라이브러리 함수 선언을 포함하는 헤더 파일을 포함할 수 있게 해주지만, 동일한 헤더 파일이 C 프로그램과 공유되는 경우 extern "C" (C에서는 허용되지 않음)는 적절한 #ifdef , 일반적으로 __cplusplus 로 숨겨져야 합니다:
#ifdef __cplusplus extern "C" int foo(int, int); // C++ 컴파일러가 이 코드를 봄 #else int foo(int, int); // C 컴파일러가 이 코드를 봄 #endif
"C"와 "C++" 언어 링크로 함수 타입을 구분하는 유일한 현대 컴파일러는 Oracle Studio이며, 다른 컴파일러들은 언어 링크만 다른 오버로드를 허용하지 않습니다. 이는 C++ 표준에서 요구하는 오버로드 집합( std::qsort , std::bsearch , std::signal , std::atexit , 그리고 std::at_quick_exit )을 포함합니다: GCC 버그 2316 , Clang 버그 6277 , CWG 이슈 1555 .
extern "C" using c_predfun = int(const void*, const void*); extern "C++" using cpp_predfun = int(const void*, const void*); // 형식적으로 잘못되었으나, 대부분의 컴파일러에서 허용됨 static_assert(std::is_same<c_predfun, cpp_predfun>::value, "C와 C++ 언어 링크는 함수 타입을 구분하지 않아야 합니다."); // 다음 선언들은 대부분의 컴파일러에서 오버로드로 선언되지 않음 // c_predfun과 cpp_predfun이 동일한 타입으로 간주되기 때문 void qsort(void* base, std::size_t nmemb, std::size_t size, c_predfun* compar); void qsort(void* base, std::size_t nmemb, std::size_t size, cpp_predfun* compar);
키워드
결함 보고서
다음의 동작 변경 결함 보고서들은 이전에 발표된 C++ 표준에 소급 적용되었습니다.
| DR | 적용 대상 | 게시된 동작 | 올바른 동작 |
|---|---|---|---|
| CWG 4 | C++98 | 내부 링크age를 가진 이름이 언어 링크age를 가질 수 있음 | 외부 링크age를 가진 이름으로 제한됨 |
| CWG 341 | C++98 |
"C"
언어 링크age를 가진 함수가
전역 변수와 같은 이름을 가질 수 있음 |
이 경우 프로그램이 잘못된 형태임
(서로 다른 번역 단위에 나타나는 경우 진단 메시지 불필요) |
| CWG 564 | C++98 |
두 선언이 언어 링크age 명세에서만 다른 경우
(즉, 'extern' 뒤에 오는 문자열 리터럴이 다른 경우) 프로그램이 잘못된 형태였음 |
대신 선언에서 주어진 실제 언어 링크age를
비교함 |
| CWG 2460 | C++20 |
후행
requires
절을 가진 friend 함수와
"C" 언어 링크age가 충돌하는 동작을 가짐 |
이 경우
"C"
언어 링크age는
무시됨 |
| CWG 2483 | C++98 |
"C"
언어 블록에 나타나는 정적 멤버 함수의
타입 링크age가 "C++" 였음 |
링크age는 "C" 임 |
참조문헌
- C++23 표준 (ISO/IEC 14882:2024):
-
- 9.11 링크 규격 [dcl.link]
- C++20 표준 (ISO/IEC 14882:2020):
-
- 9.11 링크 규격 [dcl.link]
- C++17 표준 (ISO/IEC 14882:2017):
-
- 10.5 링크 규격 [dcl.link]
- C++14 표준(ISO/IEC 14882:2014):
-
- 7.5 링크 규격 [dcl.link]
- C++11 표준 (ISO/IEC 14882:2011):
-
- 7.5 링크 규격 [dcl.link]
- C++03 표준(ISO/IEC 14882:2003):
-
- 7.5 링크 규격 [dcl.link]
- C++98 표준(ISO/IEC 14882:1998):
-
- 7.5 링크 규격 [dcl.link]