18.2.3.2 C 매크로 `ORB_DECLARE()` 및 `ORB_DEFINE()`의 전처리(Pre-processing) 후 코드 전개(Expansion) 과정

18.2.3.2 C 매크로 ORB_DECLARE()ORB_DEFINE()의 전처리(Pre-processing) 후 코드 전개(Expansion) 과정

18.2.3.1절에서 보았듯, orb_metadata 구조체는 uORB 미들웨어의 작동을 위한 필수적인 명세서이다. 하지만 100개가 넘는 토픽 각각에 대해 이 복잡한 구조체의 껍데기(선언부)와 알맹이(정의부)를 일일이 하드코딩하는 것은 매우 비효율적이며 유지보수를 불가능하게 만든다.

PX4 아키텍트들은 이 지루하고 에러가 발생하기 쉬운 볼륨 코드를 우아하게 추상화하기 위해 C 언어의 강력한 전처리기(Preprocessor) 매크로(Macro) 시스템을 도입했다. 파이썬 스크립트에 의해 자동 생성되는 헤더 파일(.h)과 소스 파일(.cpp)에는 ORB_DECLARE()ORB_DEFINE() 이라는 두 개의 매크로 함수가 뼈벽처럼 박혀 있다.

컴파일러가 코드를 해석하기 직전(Pre-processing 단계)에 이 매크로들이 어떻게 거대한 메타데이터 블록으로 폭발적으로 전개(Expansion)되는지 그 과정을 해부해 본다.

1. 헤더 파일의 ORB_DECLARE() 전개

자동 생성된 vehicle_gps_position.h 파일의 끝자락에는 다음과 같이 단 한 줄의 코드가 존재한다.

ORB_DECLARE(vehicle_gps_position);

C 전처리기가 이 코드를 만나면, uORB.h에 정의된 매크로 규칙(extern const struct orb_metadata __orb_##name)을 따라 단순 텍스트 치환을 수행한다. ## 연산자는 토큰 결합 연산자로, 파라미터로 넘어온 이름을 문자열에 그대로 이어 붙인다. 최종적으로 컴파일러에게 식별되는 코드는 다음과 같다.

// 전처리 후 전개된 결과
extern const struct orb_metadata __orb_vehicle_gps_position;

이 한 줄은 컴파일러에게 __orb_vehicle_gps_position 이라는 상수 데이터 구조체가 다른 어떤 .cpp 파일 어딘가에 메모리를 할당받아 존재하고 있으니, 링커(Linker)를 믿고 일단 통과시켜라” 라는 의미를 지닌다. 이를 통해 이 헤더를 인클루드하는 모든 모듈(발행자/구독자)은 메타데이터 주소값에 자유롭게 접근할 수 있는 권한을 얻게 된다.

2. 소스 파일의 ORB_DEFINE() 전개와 메모리 인스턴스화

실제 메타데이터가 메모리를 점유하며 탄생하는 곳은 자동 생성된 .cpp 파일 내부이다. 소스 파일에는 다음과 같은 매크로가 박혀 있다.

ORB_DEFINE(vehicle_gps_position, struct vehicle_gps_position_s, 84, __orb_vehicle_gps_position_fields, 0);

이 매크로는 토픽 이름, 바인딩할 구조체 이름, 크기 정보, 그리고 포맷 스트링 포인터 등 5개의 인자를 받는다. C 전처리기가 가동되면 이 단 한 줄의 매크로는 다음과 같은 장엄한 C++ 객체 생성 코드 블록으로 치환(Expansion)된다.

// 전처리 후 전개된 결과
const struct orb_metadata __orb_vehicle_gps_position = {
    "vehicle_gps_position",                       // o_name (토픽 이름 문자열)
    sizeof(struct vehicle_gps_position_s),        // o_size (컴파일러 산출 바이트 덩치)
    84,                                           // o_size_no_padding (파이썬이 산출한 순수 크기)
    __orb_vehicle_gps_position_fields,            // o_fields (로깅용 포맷 스트링 주소)
    0                                             // 퍼블리셔 우선순위 등 예비용 플래그
};

바로 이 순간, 링커 공간에 .rodata (Read-only Data) 섹션으로서 변경 불가능한 메타데이터 상수형 객체가 메모리에 영구적으로 마운트되는 것이다.

3. 매크로 추상화의 아키텍처적 가치

그렇다면 왜 굳이 파이썬 템플릿 엔진 단계에서부터 전개된 결과물(전체 구조체 코드)을 그대로 출력하지 않고, 굳이 C 매크로라는 중간 단계를 하나 더 거쳤을까? 이는 의존성과 유연성을 극대화하기 위한 설계이다.

만약 미래의 PX4 업데이트에서 보안을 위해 orb_metadata 구조체 안에 uint32_t topic_hash 멤버를 새롭게 추가해야 한다고 가정해 보자.
매크로 추상화가 없었다면 100개가 넘는 파이썬 생성 로직을 뜯어고친 뒤 모든 구조체 인스턴스를 갈아엎어야 한다. 하지만 매크로를 사용함으로써 코어 개발팀은 단지 uORB.h 파일 하나 속의 ORB_DEFINE 선언부만 살짝 수정해 주면 된다. 다음번 빌드 파이프라인 가동 시, 100여 개의 토픽들은 변경된 전처리기 규칙에 따라 동적으로 해시(Hash) 필드를 포함하며 알아서 새로운 구조의 메타데이터 블록으로 진화하게 된다.