헤더 포함 정리 및 등급 분리: 드라이버 외부에서 AI C++ 패치 테스트하기
요약
AI C++ 패치 테스트 시, 기존 평가 드라이버(eval driver)가 헤더 위생을 제대로 검증하지 못하는 사각지대가 존재합니다. 이는 기능적 grader는 통과시키지만, 독립적인 소비자 파일에서는 컴파일 오류를 일으킬 수 있습니다. 따라서 외부에서 동작하는 '프로브' 하네스를 도입하여 진정한 헤더 포함성을 검사해야 합니다.
핵심 포인트
- 기존 평가 드라이버는 편의성이 높아 헤더 위생을 제대로 증명하지 못합니다.
- 단순히 녹색 종료 코드가 나온다고 해서 모든 것이 완벽하게 작동하는 것은 아닙니다.
- 외부에서 동작하는 '프로브' 하네스를 도입하여 독립적인 컴파일 가능성을 검사해야 합니다.
- 헤더 파일은 자신이 실제로 사용하는 헤더를 명시적으로 포함하도록 수정되어야 합니다.
이 계정에서 측정된 사고는 아니지만, 합성 야간 빌드 장면은 여전히 실패하는 C++ 평가(evals)가 배포되고 있음을 보여줍니다. 한 grader가 02:00 직후에 패치를 승인했습니다. 모든 golden assertion은 통과했고, diff는 inventory.h와 inventory.cpp만 건드렸습니다.
다음 소비자 파일이 해당 헤더를 포함했으며 다른 것은 아무것도 포함하지 않았습니다. 컴파일은 std::string을 알 수 없다는 이유로 중단되었습니다. 평가 드라이버(eval driver)는 테스트 대상 헤더를 로드하기 전에 이미 <string>, <vector>, 그리고 프로젝트 프리앰블(prelude)을 포함했습니다.
패치는 드라이버가 이미 include 작업을 수행했기 때문에 완성된 것처럼 보였습니다.
이러한 실패 모드는 조용합니다. 기능적 grader는 결코 이를 발견하지 못합니다. 독립적인 translation-unit 게이트만이 발견할 수 있습니다.
녹색 실행이 실제로 증명하는 것
드라이버는 편의 프로그램입니다. 함수를 호출하고 반환 값을 확인하기 위해 존재합니다. 이는 헤더 위생(header hygiene)에 대한 나쁜 증거입니다.
모델은 <vector>를 포함하지 않고도 std::vector를 사용하는 공개 헤더를 내보낼 수 있으며, 드라이버의 첫 줄에서 이미 해당 헤더가 가져와졌기 때문에 여전히 컴파일될 수 있습니다. 동일한 사각지대는 누락된 전방 선언(forward declarations), unity 프리픽스(prefix)에 의해 제공되는 매크로, 그리고 독립적인 소비자 파일이 결코 받지 못할 프리컴파일 헤더(precompiled header)를 숨깁니다. 따라서 통과하는 드라이버 컴파일은 상태 표시줄이 시사하는 것보다 적게 증명합니다.
유용한 grader는 두 가지 검사를 분리합니다. 한 검사는 동작이 golden 케이스와 일치하는지 묻습니다. 다른 하나는 외부인이 깨끗한 translation unit에서 공개 헤더를 포함하고 작은 호출을 컴파일할 수 있는지 묻습니다. 단일 녹색 종료 코드는 이 검사들을 혼합하여 두 번째 실패를 숨깁니다.
하네스(harness)가 방출해야 할 프로브
아래 파일들은 제안된 워크플로우입니다. 이는 기록된 실행이 아니며 벤치마크도 아닙니다. 운영자는 이들을 패치 디렉토리, 공개 헤더 목록, 그리고 컴파일러 명령어에 가리킵니다. 그 명령어는 로컬 툴체인 또는 깨끗한 원격 트리(remote tree)를 대상으로 할 수 있습니다.
프로브는 기존 기능적 grader 옆에 유지해야 하며, 내부에 두어서는 안 됩니다. public_headers.txt는 다운스트림 파일이 포함하도록 허용된 헤더 이름만 명시합니다:
include/inventory.h
게이트의 필요성을 야기하는 깨진 헤더는 다음과 같습니다:
#pragma once
namespace inventory {
struct Item {
...
이미 <string>을 포함한 드라이버는 해당 헤더를 통해 호출을 컴파일할 수 있습니다. 하지만 독립적인 소비자(standalone consumer)는 그렇지 못합니다. 수정된 형태는 자신이 사용하는 것을 포함합니다:
#pragma once
#include <string>
namespace inventory {
...
probe_main.cpp.in에는 이미 골든 케이스에 존재하는 호출이 하나 들어 있습니다. 이 하네스(harness)는 헤더 경로를 대체합니다. 모델에게 새로운 사양을 요청하지 않으며, 헤더 안에 예상 출력 테이블을 포함시키지도 않습니다.
#include "HEADER_PATH"
int main() {
...
반환 값은 컴파일러가 호출을 인스턴스화해야 하므로 단지 활성(liveness) 확인일 뿐입니다. 이것이 행동 오라클(behavioral oracle)은 아닙니다. 행동의 진실성은 골든 케이스 레인에 남아 있습니다.
단계별 지침 (Numbered steps)
-
패치된 트리를 임시 디렉터리로 복사합니다. 이 레인만을 위해 접두사-헤더 플래그(예:
-include prelude.h)를 제거하고, 미리 컴파일된 헤더나 유니티 빌드 파일은 재사용하지 않습니다. 기능적 레인은 원래의 플래그를 유지하여 로직 회귀가 여전히 자체 신호를 갖도록 합니다. -
public_headers.txt를 읽습니다. 템플릿에서 공용 헤더(public header) 각각에 대해 하나의probe_main.cpp를 생성합니다. 이 생성된 파일은 해당 단일 공용 헤더를 포함할 수 있습니다. 하지만 평가 드라이버(eval driver), 프리루드(prelude), 또는 모든 것을 포괄하는 프로젝트 헤더는 포함해서는 안 됩니다. -
프로브를 자체 변환 단위로 컴파일합니다. 명령에서 언어 모드를 고정하여 원격 기본 설정이 패스의 의미를 조용히 변경할 수 없도록 합니다. 이 호출은 시작점일 뿐, 보편적인 CI 정책은 아닙니다:
sed "s|HEADER_PATH|inventory.h|" probe_main.cpp.in > build/probe_inventory.cpp
c++ -std=c++20 -Wall -Wextra -Werror -I include -c build/probe_inventory.cpp -o build/probe_inventory.o
-
기능적 등급을 읽기 전에 컴파일러 결과를 분류합니다. 선언 누락(missing declaration), 불완전한 타입(incomplete type) 또는 알 수 없는 이름은 모든 드라이버 단언문(driver assertion)이 통과했더라도 위생 실패(hygiene failure)입니다. 성공적인 오브젝트 파일은 이 레인만으로는 합격입니다.
-
다음 실행에서 비교할 수 있는 한 줄 기록을 추가합니다. 이 기록이 게이트 역할을 합니다. 대시보드는 선택 사항입니다.
case=inventory header=include/inventory.h lane=standalone_tu status=fail class=missing_declaration
diff -u grades/last.txt grades/this.txt
-
원본 드라이버를 사용하여 별도의 프로세스에서 기능적 골든 케이스(functional golden cases)를 실행합니다. 위생 실패 후에도 이를 건너뛰지 마십시오. 패치는 자급자족적이면서도 여전히 틀릴 수 있습니다. 패치는 행동적으로는 올바르지만 깨끗한 인클루드에서는 여전히 사용할 수 없을 수 있습니다. 보고서는 두 행을 모두 보여줘야 합니다.
-
개발자 머신에서 한 번도 빌드된 적이 없는 두 번째 트리를 대상으로 컴파일을 반복합니다. 유용한 속성은 로컬 사전컴파일 헤더(local precompiled headers)와 캐시된 unity 출력물의 부재입니다. 호스트 크기, 지역 및 가동 시간은 등급의 범위 밖에 있습니다.
작은 분류기 (A small classifier)
컴파일러 텍스트를 리포지토리가 소유한 래퍼(wrapper) 내에서 안정적인 클래스로 매핑합니다. 아래 패턴들은 제안입니다. 컴파일러나 언어 팩이 변경될 때 조정이 필요합니다.
#!/bin/sh
log="$1"
if grep -E -q "error: .*(does not name a type|was not declared|incomplete type|no member named)" "$log"; then
...
종료 코드(exit code)를 계약으로 취급합니다. 로그의 문장은 증거일 뿐, 등급의 핵심 키가 아닙니다. 나중에 컴파일러가 업그레이드되더라도 하네스(harness)가 저장하는 종료 상태는 변경하지 않으면서 문장을 바꿀 수 있습니다.
의사 결정 테이블 (Decision table)
| Probe class | Functional grade | Report | Merge? |
|---|---|---|---|
| clean | pass | both lanes green | eligible for review |
| ... | |||
| Skip은 녹색이 아닙니다. 컴파일러, 헤더 목록 또는 스크래치 트리를 찾을 수 없는 하네스(harness)는 불완전한 등급을 기록해야 합니다. 그렇지 않으면 누락된 도구는 조용한 통과(silent pass)가 되는데, 이는 월드(world)를 포함하는 드라이버의 실수와 같은 종류의 실수입니다. |
호스팅 컴파일이 적합한 경우
반복(Iteration)은 레인에서 비용이 많이 드는 부분입니다. 실패한 프로브는 누락된 include나 불완전한 타입을 명명합니다. 코딩 모델은 수리를 제안할 수 있습니다. 프로브는 그 수리를 받아들이거나 거부합니다.
모델 자체가 등급을 매기는 것은 아닙니다. 더 녹색인(greener) 제안이라도 clean translation unit을 통과해야 합니다. 이 분리가 바로 golden cases와 include probe를 서로 다른 프로세스에 유지하는 전체 목적입니다.
공개 정보: 본 문서는 MonkeyCode의 제품 아웃리치(product outreach)의 일부로 작성되었습니다. 해당 아웃리치는 이 루프(loop)에 대한 무료 모델 접근 및 무료 서버 옵션을 설명합니다. 즉, 헤더 수리를 요청한 다음, 노트북의 사전 컴파일된 헤더를 공유하지 않는 clean tree에서 독립형 프로브를 컴파일하는 것입니다. 이 두 가지 가용성 주장은 운영자(operator)가 제공한 것입니다.
본 문서는 토큰 할당량, 하드웨어 사양, 기간 또는 영구적인 제안을 명시하지 않습니다. 왜냐하면 그러한 세부 정보에 대한 주요 출처가 첨부되지 않았기 때문입니다. 팀이 이에 의존하기 전에 프로젝트 페이지에서 현재 제한 사항, 모델 목록 및 서버 약관을 확인하십시오. 게이트를 실행하는 데는 로컬 컴파일러만으로 충분합니다.
호스팅 컴파일을 유일한 증인(only witness)이 아니라 두 번째 증인(second witness)으로 사용하십시오. 원격 로그와 로컬 로그가 불일치하면 둘 다 보관하십시오. 불일치는 결과입니다. 이는 종종 숨겨진 -include 플래그, 다른 기본 언어 모드 또는 주변 include 경로를 통해 발견된 헤더를 의미합니다.
제한 사항
이 게이트는 include-what-you-use나 링크 테스트(link test), 또는 산티타이저 빌드(sanitizer build)를 대체하지 않습니다. 헤더가 <string>을 포함하더라도 임시 객체(temporary)에 대한 참조를 반환할 수 있습니다. 샘플의 const std::string& 접근자는 이러한 격차를 상기시키는 것이지, 이를 해결하는 것은 아닙니다. 매크로가 많은 헤더는 하나의 프로브 호출(probe call)을 통과했지만 다른 기능 플래그(feature flag)에서는 실패할 수 있습니다.
클래시파이어의 정규 표현식은 컴파일러별이며 다른 언어에서 발생한 진단 메시지(diagnostic)를 놓칠 것입니다. 또한 이 워크플로우는 운영자(operator)가 어떤 헤더가 공개적인지 알고 있다고 가정합니다. 내부 헤더에 포인터를 지정하면 합법적인 계층화(legal layering)에서도 문제가 생길 수 있습니다.
이미 격리된 이미지(hermetic image) 내에서 모든 공개 헤더를 컴파일하고 고정된 컴파일러를 사용하는 팀은 이 작업의 중복본을 통해 얻는 것이 거의 없습니다. 문서화된 계약이 단일 우산 헤더(umbrella header)인 프로젝트는 내부 파일 각각을 테스트하기보다 우산 헤더 자체를 프로브해야 합니다. 무료 허용 범위나 무료 호스트에 대한 재현 가능한 주장(reproducible claim)이 필요한 독자는 인용구로 이 워크스루(walkthrough)가 아니라 현재 프로젝트 문서를 사용해야 합니다.
마무리
세상을 포함하는 드라이버는 계속해서 불완전한 헤더를 축하할 것입니다. 등급을 분리하십시오. 한 레인에서는 골든 케이스(golden cases)에 대한 동작을 확인합니다. 다른 레인은 공개 헤더만 포함하는 낯선 사람의 파일을 컴파일합니다.
두 행 모두 기록하십시오. 두 레인 모두 하네스(harness)로부터 include를 빌릴 필요가 없을 때만 패치를 검토용으로 보냅니다. 녹색 단언 로그(green assertion log) 자체는 해당 헤더가 독립적으로 설 수 있다는 증거가 아닙니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기