21.5.2. DEFINE_PARAMETERS 매크로의 메타데이터 변환 과정
부모의 족보(ModuleParams)를 상속받은 CustomApp 모듈은 파라미터를 담을 수 있는 튼튼한 그릇이 되었다. 이제 이 빈 그릇 안에 실제로 값을 저장할 변수(int, float)들을 채워 넣을 차례다.
하지만 PX4 시스템에서는 헤더 파일에 float max_speed; 라고 변수를 덜렁 선언해서는 외부(QGroundControl)와 절대 소통하지 못한다.
대신, 우리는 PX4 코어팀이 심혈을 기울여 만든 극도로 난해하고 강력한 C 전처리기 매크로 군단인 **DEFINE_PARAMETERS**를 사용하여 변수를 선언해야만 한다.
1. 괄호 속의 마법: 매크로의 선언 문법
일반적인 파라미터가 포함된 모듈의 헤더 파일(.h) 말미에는 항상 다음과 같은 매크로 블록이 자리 잡고 있다.
class CustomApp : public ModuleBase<CustomApp>, public ModuleParams
{
// ... 일반 멤버 변수 및 함수 선언 ...
// 파라미터 선언 블록 시작
DEFINE_PARAMETERS(
(ParamInt<px4::params::CUSTOM_APP_MODE>) _param_custom_app_mode,
(ParamFloat<px4::params::CUSTOM_APP_MAX>) _param_custom_app_max,
(ParamFloat<px4::params::CUSTOM_APP_MIN>) _param_custom_app_min
)
};
이 코드 조각은 겉보기에는 이상한 괄호의 연속일 뿐이다. 심지어 콤마(,)로 구분된 인자들을 받고 있는데 자료형과 변수명 사이에 또 소괄호가 쳐져 있어 C++ 문법 파괴처럼 보인다.
하지만 컴파일러의 전처리기(Preprocessor)가 동작하는 순간, 저 짧은 매크로 블록은 수십 줄에 달하는 거대한 구조체 선언과 파라미터 업데이트 바인딩 코드로 극적으로 팽창(Expansion)한다.
이 파이프라인의 종착지는 단순히 내 C++ 모듈의 변수에 값을 밀어 넣는 데서 끝나지 않는다. PX4 빌드 툴체인(Python 스크립트)은 펌웨어를 컴파일하는 동안 소스 코드를 스캐닝하여 이 매크로 블록들을 싹 다 긁어모은다.
2. 코드 밖을 벗어나는 메타데이터(Metadata)의 탄생
이 파이프라인의 진정한 목적은 펌웨어 내부의 변수를 넘어, 인간이 읽을 수 있는 문서를 자동으로 생성하는 데 있다.
빌드 시스템은 CUSTOM_APP_MAX라는 심볼을 추적하여 C++ 파일 근처에 정의된 별도의 parameters.c 나 *.yaml 정의 파일을 찾아낸다. 거기서 이 파라미터의 최댓값, 최솟값, 기본값, 그리고 “이 값은 기체의 최대 속도를 제한합니다“라는 한글/영문 설명서 텍스트를 추출한다.
이 데이터들은 최종적으로 거대한 하나의 XML (또는 JSON) 메타데이터 파일로 압축되어 픽스호크 펌웨어 바이트코드 끄머리에 함께 패키징(Packaging)된다.
비행장에 나간 여러분이 노트북과 픽스호크를 USB로 연결하는 그 찰나의 순간, QGroundControl은 펌웨어 안에 숨겨진 이 거대한 XML 메타데이터를 순간적으로 다운로드받아 화면에 예쁜 슬라이더와 툴팁(Tooltip) 텍스트를 구성해 낸다.
즉, 우리가 무심코 적어 넣은 저 DEFINE_PARAMETERS 매크로 한 줄은, 컴파일러 시점을 넘어서 외부 그라운드 스테이션(GCS) UI까지 생성해버리는 Full-Stack 코드인 셈이다.
그렇다면 저 매크로의 괄호 안쪽에 있는 ParamInt 템플릿의 정체는 무엇이고, 변수가 메모리에 ఎలా(어떻게) 꽂히는지 구체적인 바인딩(Binding) 메커니즘을 다음 두 단원(21.5.2.1, 21.5.2.1.1)에서 현미경처럼 들여다보겠다.