21.5.2.1. ParamInt 및 ParamFloat 템플릿의 메모리 바인딩
PX4 파라미터 서버(QGroundControl에서 보이는 수천 개의 변수 묶음)는 본질적으로 C 언어 기반의 투박한 API(param_get(), param_set())로 이루어져 있다.
만약 C++ 개발자인 우리가 파라미터 값을 하나 읽어오려고 할 때마다 날것의 C API를 직접 호출한다면, 코드는 삽시간에 포인터와 void* 캐스팅으로 뒤덮여 스파게티가 되고 말 것이다.
이 C와 C++ 사이의 불쾌한 간극을 우아하게 메워주는 것이 바로 DEFINE_PARAMETERS 매크로 괄호 안에 들어있던 ParamInt 와 ParamFloat 템플릿 클래스이다.
1. 원시 타입(Primitive Type)의 스마트한 래퍼(Wrapper)
ParamInt (정수형 파라미터)와 ParamFloat (실수형 파라미터)는 단순한 int, float 변수가 아니다. 이 녀석들은 **자신의 뱃속에 진짜 값(Value)과 함께, 자신이 글로벌 파라미터 서버의 어떤 족보(Handle)에 속해 있는지를 기억하는 똑똑한 객체(Object)**이다.
// 매크로 안에 선언했던 형태
(ParamInt<px4::params::CUSTOM_APP_MODE>) _param_custom_app_mode,
위의 형태를 C++ 클래스 멤버 변수 관점에서 해석해 보자.
- 변수의 이름 (
_param...):_param_custom_app_mode는 이 객체의 실질적인 변수명이다. 우리는 나중에 C++ 코드 안에서if (_param_custom_app_mode.get() == 1)처럼 객체의 메서드를 호출해 진짜 값을 빼내올 수 있다. - 타입의 엄격함 (
ParamInt): 이 변수가 무조건int32_t크기의 정수만을 다루겠다는 강렬한 선언이다. C의param_get()함수는 타입을 가리지 않고void*로 데이터를 밀어 넣지만, 이 래퍼 클래스는 내부적으로 단단한 철벽을 쳐서 플로트(Float) 값이 들어오는 것을 컴파일 타임에 박살 낸다. - 메모리의 좌표 (
<px4::params::CUSTOM_APP_MODE>): 가장 중요한 템플릿 인자이다. 이것은 PX4 시스템 전체가 공유하는 거대한 파라미터 열거형(Enum) 족보표 안에서, “내 변수가 1542번째 칸(CUSTOM_APP_MODE)에 꽂혀있는 그 데이터다!“라고 좌표를 찍어주는 행위이다.
2. 메모리 바인딩(Binding)의 경이로움
이렇게 좌표를 찍어두고 객체를 생성하면, 앞서 21.5.1 단원에서 배웠던 거대한 파라미터 트리(ModuleParams)가 updateParams()를 외칠 때 엄청난 마법이 일어난다.
최상위 포인터가 “여기 CUSTOM_APP_MODE 값이 QGC로부터 새로 들어왔다!“라고 소리치면, 저 ParamInt 객체 내부의 update() 메서드가 조용히 깨어난다.
놀랍게도 우리는 어떠한 코딩도 하지 않았지만, 이 객체는 스스로 C API인 param_get()을 호출하여 글로벌 저장소에서 최신 int32_t 값을 몰래 빨아들인 뒤 자신의 뱃속 멤버 변수에 안전하게 덮어써 버린다.
즉, **나의 C++ 램(RAM) 메모리의 로컬 변수와 SD 카드에 박혀있는 글로벌 파라미터 데이터베이스가 영구적으로 결속(Binding)**된 것이다. 우리는 그저 get() 함수를 통해 마음 편히 값을 꺼내 쓰기만 하면 된다.
그렇다면 QGC 화면에 예쁘게 떠오르는 숫자의 최댓값, 최솟값, 설명서 등은 어떻게 저 px4::params::CUSTOM_APP_MODE 라는 영문자 심볼 하나에서 매직처럼 창조되는 것일까? 다음 장(21.5.2.1.1)에서 메타데이터 추출용 스크립트 파이프라인의 실체를 완전히 까발려보겠다.