C ++ : C ++ 기능이 아닌 컴파일러 API를 사용한 메타 프로그래밍


10

이것은 SO 질문으로 시작되었지만 웹 사이트의 실제 설명을 기반으로 매우 전통적이지 않다는 것을 깨달았습니다. 질문이 개념적 무게가 많기 때문에 programmers.se에 더 적합 할 수 있습니다.

나는 clang LibTooling을 배우고 있으며 코드의 전체 " 미묘한 "전체를 친숙한 방식으로, 즉 시맨틱 한 방식으로, 그리고 추측하지 않고 노출시킬 수있는 매우 강력한 도구 입니다. clang이 코드를 컴파일 할 수 있으면 clang은 해당 코드 내의 모든 단일 문자의 의미에 대해 확신 합니다.

이제 잠시 뒤로 물러서겠습니다.

C ++ 템플릿 메타 프로그래밍에 참여할 때 (특히 템플릿을 넘어서서 영리한 영역에도 불구하고 매크로를 영리하게 만들 때) 발생할 수있는 실질적인 문제가 많이 있습니다. 솔직히 말하면, 많은 프로그래머들에게, 템플릿을 많이 사용하는 것도 다소 무섭습니다.

좋은 예는 컴파일 타임 문자열 일 것 입니다. 이것은 1 년이 넘은 질문이지만 지금 당장 C ++이 단순한 필사자에게는 이것을 쉽게 만들지 못한다는 것이 분명합니다. 이러한 옵션을 살펴 보는 것만으로는 메스꺼움을 유발할만큼 충분하지는 않지만, 소프트웨어에 적용 할 수있는 멋진 응용 프로그램에 적합하도록 마법적이고 최대로 효율적인 기계 코드를 생성 할 수 있다는 확신이 들지 않습니다.

내 말은, 직면하자, 사람들, 문자열은 매우 간단하고 기본입니다. 우리 중 일부는 간단한 코드를 작성할 때 얻을 수있는 것보다 특정 문자열이 "베이크 인 된"기계 코드를 생성하는 편리한 방법을 원합니다. C ++ 코드에서.

clang 및 LibTooling을 입력하면 소스 코드의 추상 구문 트리 (AST)가 노출되고 간단한 사용자 정의 C ++ 애플리케이션 Rewriter이 AST의 모든 의미에 대한 풍부한 의미 론적 객체 지향 모델과 함께 원시 소스 코드 ( ) 를 정확하고 안정적으로 조작 할 수 있습니다. 많은 것을 처리합니다. 매크로 확장에 대해 알고 체인을 따라갈 수 있습니다. 예, 소스-소스 코드 변환 또는 번역에 대해 이야기하고 있습니다.

필자의 기본 논문은 clang을 사용하여 C ++ 소프트웨어에 대한 이상적인 사용자 지정 전 처리기 단계로 작동 할 수있는 실행 파일을 만들 수 있으며 이러한 메타 프로그래밍 단계를 C ++로 구현할 수 있다는 것입니다. 이 단계는 유효한 C ++ 코드 인 입력을 가져와보다 유효한 C ++ 코드를 출력으로 생성해야한다는 사실에 의해 제약을받습니다. 또한 빌드 시스템에 적용되는 다른 제한 사항이 있습니다.

clang은 컴파일러 프론트 엔드이며 API를 사용하여 창의력을 발휘하기 때문에 입력은 적어도 유효한 C ++ 코드에 매우 가깝습니다. 사용할 새 구문을 정의 할 수있는 조항이 있는지는 잘 모르겠지만,이를 위해서는 구문을 올바르게 구문 분석하고이를 clang 프로젝트에 추가하는 방법을 개발해야합니다. 더 이상 기대하는 것은 범위를 벗어난 clang 프로젝트에 무언가를 갖는 것입니다.

문제가 아니다. 나는 어떤 no-op 매크로 함수 가이 작업을 처리 할 수 ​​있다고 상상할 것입니다.

내가 설명하는 것을 보는 또 다른 방법은 언어 자체에서 사용할 수있는보다 제한된 도구를 사용하여 소스 코드의 AST (clang 및 API 덕분에)를 조작하는 대신 런타임 C ++을 사용하여 메타 프로그래밍 구문을 구현하는 것입니다. 이것은 명백한 컴파일 성능 이점도 가지고 있습니다 (템플릿이 많은 헤더는 사용 빈도에 비례하여 컴파일 속도가 느려집니다. 많은 컴파일 된 항목이 링커에 의해 조심스럽게 일치되어 버려집니다).

그러나 이것은 빌드 프로세스에서 추가 단계를 도입하고 비용이 다소 들며, 좀 더 장황한 소프트웨어를 작성해야 할 필요가 있습니다 (그러나 최소한 런타임 C ++입니다). .

그것은 전체 그림이 아닙니다. 핵심 언어 기능으로는 매우 어렵거나 불가능한 코드를 생성하여 얻을 수있는 훨씬 더 큰 기능 공간이 있다고 확신합니다. C ++에서는 템플릿이나 매크로 또는 둘 다의 미친 조합을 작성할 수 있지만, clang 도구 에서는 시맨틱 컨텐츠에 대한 전체 액세스 권한을 가지면서 런타임에 C ++로 달성 할 수있는 방식으로 클래스와 함수를 수정할 수 있습니다 . 템플릿 및 매크로 및 기타 모든 것 외에도 .

그래서 나는 왜 모두가 이미 이것을하지 않는지 궁금합니다. clang 의이 기능이 너무 새롭고 아무도 clang의 AST의 거대한 클래스 계층 구조에 익숙하지 않습니까? 그럴 수 없습니다.

어쩌면 나는 이것의 어려움을 약간 과소 평가하고 있지만, clang 도구로 "컴파일 타임 문자열 조작"을하는 것은 거의 범죄 적으로 간단합니다. 그것은 장황하지만, 엄청나게 간단합니다. 필요한 것은 실제 실제 std::string작업에 매핑되는 무수한 매크로 기능입니다 . clang 플러그인은 관련된 모든 no-op 매크로 호출을 가져와이를 구현하고 문자열을 사용하여 작업을 수행합니다. 그런 다음이 도구는 빌드 프로세스의 일부로 삽입됩니다. 빌드하는 동안 이러한 no-op 매크로 함수 호출은 자동으로 결과로 평가 된 다음 프로그램에서 일반 오래된 컴파일 타임 문자열로 다시 삽입됩니다. 그런 다음 평소대로 프로그램을 컴파일 할 수 있습니다. 실제로이 결과 프로그램은 결과적으로 훨씬 이식성이 뛰어나므로 C ++ 11을 지원하는 멋진 새 컴파일러가 필요하지 않습니다.


이것은 매우 긴 질문입니다. 가장 관련성이 높은 지점으로 압축 할 수 있습니까?
amon

나는 많은 긴 질문을 게시합니다. 그러나 특히 이것으로, 질문의 모든 부분이 중요하다고 생각합니다. 첫 6 개 문단을 건너 뛸 수 있습니까? ㅋ.
Steven Lu

3
리스프 (Lisp)에서 개척되고 최근에 Haxe, Nemerle, Scala 및 유사한 언어에 의해 채택 된 구문 매크로와 흡사합니다. Lisp 매크로가 왜 유해한 것으로 여겨지는지에 대한 약간의 독서가 있습니다. 아직 설득력있는 주장을 듣지는 못했지만, 사람들이 모든 언어에 언어를 추가하기를 꺼려 한 이유를 찾을 수 있습니다.
back2dos

예, 메타 화 C ++입니다. 더 나은 코드를 의미 할 수 있습니다. 언어들에 관해서 . 그럼 어디에서 시작할까요? 그러한 언어로 구현 된 수백만 달러의 비디오 게임은 무엇입니까? 해당 언어로 구현 된 최신 웹 브라우저 란 무엇입니까? OS 커널? 실제로 Haxe는 약간의 견인력을 가지고있는 것처럼 보이지만 아이디어를 얻습니다.
Steven Lu

1
@ nwp, 글쎄, 나는 당신이 게시물의 전체 요점을 놓친 것 같다는 것을 도울 수는 없지만 지적했다. 컴파일 타임 문자열은 현재 우리가 사용할 수있는 기능 중 가장 고 안되고 구체화 된 예입니다.
Steven Lu

답변:


7

예, 버지니아 산타 클로스가 있습니다.

프로그램을 수정하기 위해 프로그램을 사용한다는 개념은 오랫동안 사용되어 왔습니다. 원래의 아이디어는 존 폰 노이만 (John von Neumann) 이 저장 프로그램 컴퓨터 형태로 만들어 낸 것 입니다. 그러나 임의의 방식으로 기계 코드를 수정하는 기계 코드는 매우 불편합니다.

사람들은 일반적으로 소스 코드 를 수정하려고합니다 . 이것은 대부분 프로그램 변환 시스템 (PTS) 의 형태로 실현됩니다 .

PTS는 일반적으로 하나 이상의 프로그래밍 언어에 대해 AST로 구문 분석하고 AST를 조작하며 유효한 소스 텍스트를 재생성하는 기능을 제공합니다. 실제로 대부분의 주류 언어를 위해 누군가가 그러한 도구를 만들면 (Clang은 C ++의 예이며, Java 컴파일러는이 기능을 API로 제공하고 Microsoft는 Rosyln, Eclipse의 JDT 등을 절차 적으로 제공합니다) 실제로 매우 유용한 API입니다. 광범위한 커뮤니티의 경우 거의 모든 언어 별 커뮤니티가 다양한 수준의 성숙도 (일반적으로 겸손하고 "AST를 생성하는 많은 파서")로 구현 된 이와 같은 것을 가리킬 수 있습니다. 행복한 메타 프로그래밍.

[ 프로그래밍 언어 내부 에서 메타 프로그래밍을 시도하는 리플렉션 지향 커뮤니티가 있지만 "런타임"동작 수정 만 달성하고 언어 컴파일러 가 리플렉션을 통해 일부 정보를 사용할 수 있는 정도까지만 수행합니다 . LISP를 제외하고는 항상 리플렉션으로 할 수없는 것을 제한하는 리플렉션으로 사용할 수없는 프로그램 ( "Luke, 소스가 필요합니다")에 대한 세부 사항이 있습니다.]

보다 흥미로운 PTS는 임의의 언어에 대해이를 수행합니다 (최소한 BNF를 포함하여 도구에 언어 설명을 구성 매개 변수로 제공함). 이러한 PTS를 사용하면 "소스에서 소스로"변환을 수행 할 수 있습니다 (예 : 대상 언어의 표면 구문을 사용하여 패턴을 직접 지정) . 이러한 패턴을 사용하면 관심있는 조각을 코딩하거나 코드 조각을 찾아서 바꿀 수 있습니다. 대부분의 작업을 수행하기 위해 AST에 대한 모든 미세한 세부 사항을 알 필요가 없기 때문에 프로그래밍 API보다 훨씬 편리합니다. 이것을 메타 메타 프로그래밍이라고 생각하십시오 :-}

단점 : PTS가 다양한 종류의 유용한 정적 분석 (기호 테이블, 제어 및 데이터 흐름 분석)을 제공하지 않는 한 대부분의 실제 작업에 대한 유형을 확인하고 정보 흐름을 확인해야하므로이 방법으로 실제로 흥미로운 변환을 작성하기가 어렵습니다. 불행히도,이 기능은 일반적인 PTS에서는 드물다. (언제나 제안 된 "파서 만 있다면 ..." "파싱 후 수명"에 대한 자세한 내용은 내 바이오를 참조하십시오).

문자열 재 작성을 할 수 있다면 [따라서 트리 재 작성] 임의 변환을 수행 할 수 있다는 정리가 있습니다. 따라서 많은 PTS는 이것에 의존하여 그들이 제공하는 트리만으로 아무것도 메타 프로그래밍 할 수 있다고 주장합니다. 정리는 어떤 의미에서든 만족할 수 있지만 튜링 머신의 기능이 튜링 머신 프로그래밍을 선택 방법으로 만들지 않는 것과 같은 방식으로 만족스럽지 못합니다. (절차 적으로 API를 사용하는 시스템에서도 마찬가지입니다. AST를 임의로 변경할 수 있다면 실제로 Clang에서는 그렇지 않다고 생각합니다).

원하는 것은 언어를 매개 변수화 한 PTS 유형 (여러 언어를 처리하더라도)의 일반성을 제공하는 시스템으로, 추가적인 정적 분석과 소스 간 변환을 절차와 혼합 할 수있는 기능을 제공합니다. 아피스. 나는 이것을 하는 두 가지에 대해서만 알고 있습니다 .

  • Rascal (MPL) 메타 프로그래밍 언어
  • DMS 소프트웨어 리엔지니어링 툴킷

하지 않는 한 당신은 쓰기에게 언어의 설명과 정적 분석기 자신을 원하는 성숙한 언어 설명과 함께, 당신이 원하는 것 PTS (C를 들어 ++이 연타가 컴파일러로 일반 절차 메타 프로그래밍 기초로 모두 건설되었다 이유는 작품의 엄청난 양이다) 이미 사용 가능합니다. 그렇지 않으면 PTS를 구성하는 데 모든 시간을 할애하고 실제로 원하는 작업을 수행하지 않습니다. [주류가 아닌 임의의 언어를 선택하면이 단계를 피하기가 매우 어렵습니다].

Rascal은 "OPP"(Other People 's Parsers)를 함께 사용하여이 작업을 시도하지만 정적 분석 부분에는 도움이되지 않습니다. 나는 그들이 Java를 꽤 잘 가지고 있다고 생각하지만, C 또는 C ++을하지 않을 것이라고 확신합니다. 그러나 학술 연구 도구입니다. 그들을 비난하기 어렵다.

내가 강조하는 우리의 [상용] DMS 도구는 C ++ 전체 프런트 엔드 가능한 자바, C를 가지고있다. C ++의 경우 GCC를위한 C ++ 14의 거의 모든 내용과 심지어 Microsoft의 변형 (지금 우리는 연마 중임), 매크로 확장 및 조건부 관리, 메소드 수준 제어 및 데이터 흐름 분석을 다룹니다. 그렇습니다. 문법 변경을 실용적인 방식으로 지정할 수 있습니다 . 우리는 F90 / APL 데이터 병렬 배열 연산에 어느 정도의 양을 사용하도록 C ++를 근본적으로 확장 한 클라이언트를위한 맞춤형 VectorC ++ 시스템을 구축했습니다. DMS는 대규모 C ++ 시스템에서 다른 대규모 메타 프로그래밍 작업 (예 : 응용 프로그램 아키텍처 재구성)을 수행하는 데 사용되었습니다. (저는 DMS의 설계자입니다).

행복한 메타 메타 프로그래밍.


Clang과 DMS는 겹치는 기능이 있지만 실제로 같은 범주에 속하지 않는 소프트웨어라고 생각합니다. 내 말은, 아마도 하나는 엄청나게 비싸고 아마도 그것에 접근하는 데 필요한 자원을 정당화 할 수는 없으며 다른 하나는 무제한 무료 오픈 소스입니다. 이것은 큰 차이입니다 ... 이러한 흥미 진진한 메타 프로그래밍 기능에 대해 제가 흥분하게하는 부분은 실제로 그것을 자유롭게 사용하는 것뿐만 아니라 clang 기반 바이너리 도구를 자유롭게 배포 할 수 있다는 사실입니다.
Steven Lu

상업적으로 판매되는 모든 것은 무료에 비해 "엄청나게 비싸다". 원가는 문제가되지 않습니다. 중요한 것은 일부 사람들의 경우 상용 제품 구입에 대한 투자 회수가 무료 아티팩트에 대한 투자 회수보다 높으며, 그렇지 않으면 상용 소프트웨어가 없다는 것입니다. 이것은 분명히 당신의 특정 요구에 달려 있습니다. Clang은 공구 공간에서 흥미로운 점이며, 유용한 적용 점이 있습니다. DMS가 더 넓은 기능을 가지고 있다고 생각합니다 (DMS 설계자이므로). Clang은 C ++ 이외의 언어를 잘 지원하지 않을 수 있습니다.
Ira Baxter

확실히. DMS가 놀랍도록 강력하다는 데는 의문의 여지가 없지만 (아서 C. 클라크 (Ar Arthur C. Clarke)) 클랜은 훌륭하지만 실제로는 잘 작성된 C ++ 프론트 엔드이며 그 중 많은 것이 있습니다. 앞으로도 많은 작은 발전이 있었지만 DMS와 비교해 볼 때 여전히 공평하지는 않습니다. 아아, 우리가 처리 할 수있는 강력한 도구가 있더라도 작동하는 소프트웨어는 스스로 작성하지 않습니다. 도구를 사용하여 신중하게 번역하거나 새로 작성 된 (거의 항상 우수한 옵션) 여전히 존재해야합니다.
Steven Lu

Clang 또는 DMS와 같은 도구를 새로 만들 수는 없습니다. 일반적으로 5 년 동안 10 명으로 구성된 팀과 함께 작성한 응용 프로그램을 버릴 여유도 없습니다. 소프트웨어 크기와 수명이 계속 증가함에 따라 이러한 도구가 점점 더 자주 필요할 것입니다.
Ira Baxter

@StevenLu : 글쎄요, DMS는 칭찬에 감사하지만, 그것에 대한 마술은 없습니다. DMS는 거의 20 년에 걸친 수십 년간의 엔지니어링과 깨끗한 아키텍처 플랫폼 (aw, shucks, YMMV)의 장점을 가지고 있습니다. 마찬가지로 Clang에는 많은 훌륭한 엔지니어링 기능이 있습니다. 나는 그들이 똑같은 문제를 해결하도록 설계되지 않았다는 데 동의합니다 ... DMS의 범위는 상징적 인 프로그램 조작에 관해서는 더 커지고 프로덕션 컴파일러에 관해서는 훨씬 작도록 의도되었습니다.
Ira Baxter

4

템플릿을 사용하는 대신 컴파일러의 API를 사용하여 C ++에서 메타 프로그래밍하는 것은 실제로 흥미롭고 실제로 가능합니다. 메타 프로그래밍은 (아직) 표준화되지 않았기 때문에 특정 컴파일러와 관련이 있으며 템플릿에는 해당되지 않습니다.

그래서 나는 왜 모두가 이미 이것을하지 않는지 궁금합니다. clang 의이 기능이 너무 새롭고 아무도 clang의 AST의 거대한 클래스 계층 구조에 익숙하지 않습니까? 그럴 수 없습니다.

많은 사람들이 다른 언어로 이것을합니다. 제 의견은 대부분의 C ++ (또는 Java 또는 C) 개발자가 필요하다고 보지 않거나 메타 프로그래밍 방식에 익숙하지 않다는 것입니다. 또한 IDE의 리팩토링 / 코드 생성 기능에 만족하고 있으며 유지 관리하기가 너무 복잡하고 디버그하기 어려운 모든 것이 더 복잡하다고 생각합니다. 적절한 도구가 없으면 사실 일 수 있습니다. 직원 채용 및 / 또는 훈련과 같은 관성 및 기타 비 기술적 문제도 고려해야합니다.

그건 그렇고, 우리는 Common Lisp와 그 매크로 시스템 (Basile의 답변 참조)에 대해 언급했기 때문에 어제 Clasp 가 출시 되었다고 말해야합니다 .

Clasp 는 LLVM IR로 컴파일되는 적합한 Common Lisp 구현이 되려고합니다. 또한 Clang 라이브러리 (AST, Matcher)를 개발자에게 노출합니다.

  • 첫째, 라이브러리를 사용할 때를 제외하고 (매크로가 필요한 경우 CL 매크로를 사용하는 경우를 제외하고) CL로 작성하고 더 이상 C ++을 사용할 수 없음을 의미합니다.

  • 둘째, 기존 C ++ 코드 (분석, 리팩토링 등)를위한 도구를 CL로 작성할 수 있습니다.


3

몇몇 C ++ 컴파일러에는 문서화되고 안정적인 API, 특히 대부분의 무료 소프트웨어 컴파일러가 있습니다.

Clang / LLVM 은 대부분 큰 라이브러리 세트이며이를 사용할 수 있습니다.

최근 GCC플러그인을 허용 합니다. 특히 MELT (메타 플러그인이며 GCC를 확장하기위한 고급 도메인 특정 언어를 제공함)를 사용하여 확장 할 수 있습니다 .

C ++ 의 구문 은 GCC에서 쉽게 확장 할 수 없지만 Clang에서는 쉽게 확장 할 수 없지만 원하는 프라 그마, 내장, 속성 및 컴파일러 패스를 추가하여 원하는 작업을 수행 할 수 있습니다 (아마도 이러한 작업을 호출하는 일부 전 처리기 매크로 제공) 사용자에게 친숙한 구문을 제공하기 위해).

당신은 예를 참조 다단계 언어와 컴파일러에 관심이있을 수 하는 AST, Gensym 및 반사 사용하여 구현 다단계 언어 C.Calcagno 등에 의해 용지를. MetaOcaml을 해결 하십시오 . 반드시 Common Lisp 의 매크로 기능을 살펴 봐야합니다 . 그리고 당신에 의해 관심이있을 수 JIT 라이브러리 와 같은 libjit , GNU 번개 , 심지어 LLVM , 또는 단순히 -at 런타임! - 일부 C ++ 코드를 생성 공유 객체 동적 라이브러리로의 편집 포크, 다음 한다면 dlopen (3) 것을 공유 목적. J.Pitrat의 블로그 는 이러한 반사적 접근과도 관련이 있습니다. 또한 RefPerSys .


흥미 롭군 GCC가 계속 발전하는 것을 보는 것은 매우 좋습니다. 이것은 내가 요구 한 것을 해결하는 대답이 아니지만 그럼에도 불구하고 나는 그것을 좋아합니다.
Steven Lu

다시 : 당신의 새로운 편집 ... 그것은 코드 재 작성 자체에 대한 좋은 지적입니다. 이것은 실제로 이러한 메타 프로그램 기능을 C ++에도 가져 오기 시작했으며, 이전보다 훨씬 더 접근하기 쉬웠습니다.
Steven Lu
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.