28.1.3.1. C++ 다형성(Polymorphism) 활용 측면의 비교

28.1.3.1. C++ 다형성(Polymorphism) 활용 측면의 비교

현대 오픈소스 비행 펌웨어 생태계에서, 수십여 개의 거대한 비행 모드의 방대한 로직을 통제 관리하는 확장성(Extensibility)과 유지보수성을 극명하게 판가름하는 기술적 분수령은, 바로 객체 지향 프로그래밍(OOP)의 심장부라 할 수 있는 C++ 다형성(Polymorphism)을 커널 아키텍처 레벨에서 어떻게 채택하고 조율했는가에 달려있다.

PX4와 Ardupilot 두 펌웨어는 객체의 상속(Inheritance)과 런타임 계층의 동적 바인딩(Dynamic Binding)을 다루는 소프트웨어 공학적 설계 철학에서 커다란 체온 차이를 노골적으로 보여준다.

1. PX4: 플러그인(Plug-in) 아키텍처와 완벽한 가상 함수(Virtual Function) 다형성

PX4는 비행 모드 생성 로직에 해당하는 FlightTask 궤적 산출 계층을, C++의 철저한 순수 가상 함수(Pure Virtual Function) 기반 다이내믹 디스패치(Dynamic Dispatch) 디자인 패턴으로 설계하였다.

  • 기반 클래스(Base Class)의 하드코어 추상화: 프레임워크 코어 디렉터리 시스템에 깊숙이 존재하는 추상 부모 클래스 FlightTaskvirtual bool activate() = 0;, virtual bool update() = 0; 과 같은 구현부가 텅 빈 순수 가상 함수를 원형으로 선언하여, 모든 파생 비행 모드가 지켜야만 하는 입출력 템플릿 인터페이스 율법만을 엄격하게 제공 규정한다.
  • 하위 파생 클래스의 개별 상속 구현 로직: 조종기 스틱을 해석하는 수동 제어 모드 소스인 FlightTaskManualPosition이나, GPS를 따라가는 자동 비행 모드 소스인 FlightTaskAuto 계열 클래스들은 모두 이 단일한 통뼈 FlightTask를 상속(Inherit)받아, 오직 자신만의 톡창적인 수학적 동역학 산출 로직으로 빈껍데기 update() 함수 내부를 오버라이딩(Overriding)하여 살을 붙여 구현한다.
  • 런타임 다형성(Runtime Polymorphism)의 마법 발현: 실행 브로커 역할을 하는 FlightModeManager 메모리 클래스 내부에는 파생된 자식 모드들의 개별 클래스 포인터가 존재하지 않는다. 오직 최상위 부모 타입의 단일 포인터인 FlightTask *_current_task; 단 한 줄만 위풍당당하게 존재한다.
    비행 모드가 변경되는 런타임 찰나에, 매니저는 새 자식 객체를 램에 동적 생성하여 이 단일 부모 포인터 슬롯 하나에 냅다 꽂아버린다(Upcasting 변환).
    그리고 400Hz 메인 루프에서는 현재 장착된 타스크 종류가 미션이든, 매뉴얼이든 상관없이 무식하고 일정하게 _current_task->update(); 단 한 줄만 무한 반복 호출한다. 그러면 C++ 컴파일러의 가상 함수 테이블(vtable) 포인터가 런타임에 현재 램에 띄워진 진짜 자식 클래스의 update() 추적 로직 메모리 주소를 스스로 영민하게 찾아내어 정확히 실행을 파생 매핑해 준다.

이는 새로운 커스텀 융합형 비행 모드 클래스(예: FlightTaskMyCustomMode)를 천재 연구자가 새로 개발하더라도, 기존 코어인 FlightModeManager 소스 코드는 단 한 글자도 뜯어고칠 필요 없이, 빌드 시스템 파일(CMakeLists) 명단에 플러그인처럼 자식 클래스만 툭 얹어 밀어 넣으면 완벽하게 기존 호흡에 구동되는 **개방-폐쇄 원칙(OCP, Open-Closed Principle)**의 눈부신 표본이다.

2. Ardupilot: 정적 바인딩(Static Binding)과 스위치 분기(Switch-Case) 중심의 모놀리식 강제 결합

반면 Ardupilot은 역사적 배경으로 인해 다소 보수적이고 절차 지향적인 형태의 얕고 결속된 상속 방식을 고수한다. Mode라는 기본 부모 클래스가 구색으로 존재하긴 하지만, PX4처럼 완벽히 격리 독립된 플러그인 다형성 패턴을 따른다기보다는 거대한 중앙 Copter 메인 클래스 변수와 끈적하게 강결속된 종속적인 보조 객체 느낌이 매우 강하다.

  • 메인 매니저 루프의 거대한 스위치-케이스 분기망: 전통적인 버전의 아두파일럿 코드나 일부 최상단 틱 스케줄링 로직을 해부해 보면, 여전히 거대한 switch(control_mode) 하드코딩 분기문 열이 메인 update_flight_mode() 함수 내부에 굵직한 기둥처럼 육중하게 박혀 있다. case LOITER: 일 때는 mode_loiter.run(), case RTL: 일 때는 mode_rtl.run() 을 프로그래머가 명시적으로 구분 지어 하드코딩 호출 텍스트를 박아 넣는 방식이 깊게 혼재한다.
  • 강한 아키텍처 타입 의존성 통증: 새로운 커스텀 비행 모드를 하나 시스템에 추가(Add)하려면, 단순히 C++ 상속받은 새 독립 클래스 파일(mode_custom.cpp)을 하나 잘 짜서 만드는 것에 그치지 않는다. Copter 클래스의 메인 헤더 파일 선언부, 글로벌 모드 열거형(Enum) 상수표 목록, 그리고 메인 루프의 스위치-케이스 분기문 호출부까지 펌웨어의 코어 심장 파일들을 동시다발적으로 절개하고 무자비한 외과 수술 코딩을 감행해야만 비로소 에러 없이 시스템에 편입 컴파일될 수 있다.

3. 메모리 오버헤드 vs 개발 무한 확장성의 아키텍처 트레이드오프 심판

PX4가 맹목적으로 채택한 이 극단적 다형성 패턴은 소프트웨어 공학적으로 분명한 무거운 약점도 짊어지고 있다.
C++ 언어 규격상 virtual 가상 함수 호출은 포인터를 타고 블랙박스인 가상 함수 테이블(vtable) 주소를 실시간 런타임에 한 뎁스 더 타고 들어가 조회해야만 한다. 따라서 단순한 정적 직접 함수 호출(Direct Call) 리니어 방식에 비해 마이크로초 단위의 미세한 CPU 사이클 비용(Vtable Dispatch Overhead) 캐시 미스가 부과되며, 실시간 상황에서 객체의 생성 파괴에 따른 램(RAM) 조각화 단편화를 유발할 소지도 통계적으로 늘어난다.

하지만 현대 드론 보드에 탑재되는 32비트 고성능 ARM Cortex-M7 프로세서(보통 200~400MHz 이상 클럭)의 강력한 슈퍼스칼라 산술 환경에서는, 이깟 짧은 vtable 메모리 포인터 참조 오버헤드가 전체 비행 동역학 퍼포먼스나 제어 주기에 미치는 타격은 사실상 거의 제로(0%)에 수렴할 만큼 소거되어 미미해졌다.

결과적으로 PX4 최고 아키텍트 팀은 이 극도의 미세한 성능 하락 패널티 계측을 쿨하게 눈감아 감수하는 대신, 전 세계 수만 명의 석박사 개발자, 천재 연구원들이 PX4 코어 생태계 소스를 부작용으로 망가뜨리지 않고 오직 자신만의 참신한 제어 비행 로직 클래스를 레고 블록처럼 무한대로 플러그인 상속 개발하여 병합(PR)할 수 있는 **객체 지향적 무한 확장성(Limitless OOP Scalability)**이라는 거대한 전략적 생태계 이득을 완벽하게 쟁취해 낸 것이다.