Published On
Read Time
11분
By Micael Borgefeldt
Published On
Read Time
11분
요약
C++23이 발표되었고, C++26도 곧 뒤따를 예정입니다. 하지만 많은 펌웨어 팀을 살펴보면, 여전히 C++11로 작성된 코드나 두꺼운 매크로 층을 씌운 C 언어로 작성된 코드를 발견할 수 있습니다. 그 이유는 대개 관심 부족 때문이 아닙니다. 임베디드 팀은 해당 언어 표준을 둘러싼 전체 툴체인이 준비된 후에야 비로소 그 표준을 채택할 수 있기 때문입니다.
컴파일러가 이를 지원해야 하고, 디버거가 이를 이해해야 하며, 정적 분석 도구와 코딩 표준이 이를 포괄해야 합니다. 안전이 중요한 제품의 경우, 인증 절차에서도 이를 인정해야 합니다. 이러한 조건 중 하나라도 충족하지 못하는 언어 기능은 아무리 매력적으로 보여도 고려 대상에서 제외됩니다.
이렇게 보면, 임베디드 팀에게 중요한 질문은 어떤 C++ 표준이 가장 최신인가가 아닙니다. 자신 있게 배포할 수 있는 최신 표준이 무엇인가 하는 것입니다. 오늘날 대부분의 팀에게 그 답은 C++20입니다.
임베디드 개발은 데스크톱 및 서버 소프트웨어와는 다른 일정을 따릅니다. 제품은 10년 이상 현장에 남아 있으며, 모든 툴체인 변경 사항은 실제 생산 코드에 적용되기 전에 반드시 검증되어야 합니다. 새로운 표준이 널리 구현되고 분석 도구에서 지원되기 시작할 무렵이면, 많은 조직은 이전 표준을 대규모로 도입하기 시작한 단계에 불과합니다.
동시에 앞으로 나아가야 한다는 압박감도 커졌습니다. 펌웨어는 이제 연결성, 원격 업데이트,기능 안전성, 풍부한 사용자 인터페이스, 그리고 경우에 따라 엣지 AI 워크로드까지 처리합니다. 코드베이스가 커짐에 따라, 가장 어려운 엔지니어링 과제는 종종 기능을 처음 구현하는 것보다 긴 제품 수명 주기 동안 코드의 유지 관리성을 유지하는 것입니다. 더 강력한 타입 안전성과 명확한 추상화는 바로 이러한 문제를 해결하는 데 도움이 됩니다.
C++20은 바로 이 두 가지 요소가 만나는 지점입니다. C++20은 표현력과 안전성 측면에서 의미 있는 진전을 제공하며, 다양한 컴파일러와 툴에서 충분히 성숙할 시간을 가졌습니다.
모든 C++20 기능이 마이크로컨트롤러에서 똑같이 유용한 것은 아닙니다. 임베디드 팀이 가장 먼저 채택하는 기능들은 모두 한 가지 공통점을 가지고 있습니다. 바로 컴파일 시점에 작동하며 바이너리에 아무것도 추가하지 않는다는 점입니다.
std::bit_cast와bit 헤더의 다른 함수들은 안전하지 않은 타입 변환 및 수동으로 작성된 비트 조작을 명확하게 정의된 표준 코드로 대체합니다.다음은 컨셉트와 std::span이 실제로 어떻게 함께 작동하는지 보여주는 예시입니다. 이 로깅 함수는 쓰기 작업을 제공하는 모든 주변 장치를 받아들입니다:
#include <concepts>
#include <cstddef>
#include <span>
template <typename T>
concept Uart = requires(T port, std::byte b) {
port.write(b);
};
void log(Uart auto& port, std::span<const std::byte> msg) {
for (auto b : msg) {
port.write(b);
}
}
Uart 인터페이스를 만족하지 않는 타입을 전달하면 컴파일러가 즉시 이를 거부하며, 충족되지 않은 요구 사항을 명시합니다. span은 자체 길이를 포함하므로, 버퍼 크기가 일치하지 않는 경우 이 함수를 호출할 수 없습니다. 생성된 코드는 직접 작성했을 때와 마찬가지로 간결한 루프 형태입니다.
일부 C++20 기능은 더 신중한 접근이 필요합니다. 코루틴은 컴파일러가 달리 증명하지 않는 한 상태를 힙에 할당할 수 있는데, 이는 동적 할당을 허용하지 않는 시스템에서 중요한 문제입니다. 모듈은 강력하지만, 광범위한 생태계 전반에 걸친 빌드 툴 지원은 아직 정착 단계에 있습니다. 두 기능 모두 평가해 볼 가치가 있지만, 대부분의 팀이 이를 시작으로 삼아서는 안 됩니다.
이 두 가지 질문은 임베디드 엔지니어들이 가장 먼저 던지는 질문이며, 명확한 답변이 필요합니다.
코드 크기와 관련하여, 위에 나열된 기능들은 런타임에 아무런 비용을 발생시키지 않습니다. 일반적으로 우려되는 부분은 예외와 RTTI인데, 이 둘은 애플리케이션 크기를 증가시킬 수 있습니다. IAR C/C++ 컴파일러를 사용하면 --no_exceptions 및 --no_rtti 옵션을 통해 이 두 가지를 모두 비활성화할 수 있으므로, 각 프로젝트는 필요한 기능만 활성화하면 됩니다. 이 기능들이 활성화되어 있더라도, 실제 오버헤드는 단순히 기능이 사용 가능한지 여부가 아니라 사용 방식에 따라 달라집니다.
동적 메모리에 관해서는, 사용하는 표준 라이브러리의 구성 요소에 따라 답이 달라집니다:
std::array, std::span, std::string_view, std::optional 및 std::variant는 절대 메모리를 할당하지 않습니다.std::vector, std::string 및 std::function은 할당할 수 있으며, 구현 방식에 따라 예외 처리에 동적 메모리가 사용될 수 있습니다.엄격한 ‘힙 사용 금지’ 규칙을 따르는 팀은 일반적으로 첫 번째 그룹으로만 제한하거나 자체 할당기를 제공합니다.
이러한 구분은 결정론 측면에서도 중요합니다. 컴파일 시간 기능은 인터럽트 지연 시간이나 타이밍에 영향을 미치지 않습니다. 할당을 수행하거나 예외를 발생시킬 수 있는 모든 요소는, 잘 작성된 C 코드에서와 마찬가지로, 인터럽트 서비스 루틴 및 하드 실시간 경로에서 배제되어야 합니다.
IAR C/C++ 컴파일러는 기본적으로 C++20 소스 코드를 지원하며, ISO/IEC 14882:2020 표준을 대체로 준수합니다. Libc++ 라이브러리와 결합하면 C++20 언어 및 라이브러리 기능의 대부분을 활용할 수 있습니다.
지원은 컴파일러를 넘어 확장됩니다. IAR C-SPY 디버거는 STL 컨테이너를 읽기 쉬운 형태로 표시하며, 예외가 발생하거나 핸들러 없이 빠져나갈 때 실행을 중지할 수 있습니다. 덕분에 타겟 환경에서 최신 C++ 코드를 일반 C 코드만큼 쉽게 검사할 수 있습니다.
자동차, 산업 및 의료 시장의 팀에게 있어 언어 기능은 안전 프로세스에 부합할 때만 중요합니다.
안전 관련 C++에 대한 현재의 코딩 표준인 MISRA C++:2023은 C++17을 기반으로 합니다. 따라서 C++20 고유의 구문은 이 표준의 명시적인 지침 범위를 벗어나며, 팀은 이를 어떻게 처리할지 결정해야 합니다. 대개는 동작이 명확하고 잘 이해된 기능으로 사용을 제한하는 방식을 택합니다. IAR C-STAT은 코드를 MISRA C++에 따라 검사하여 , 팀이 결정한 하위 집합을 효과적으로 적용할 수 있도록 지원합니다.
이 기사에서 사용된 예제를 검증하기 위해, Libc++이 활성화된 IAR LLVM C/C++ 컴파일러를 사용하여 코드를 컴파일, 링크 및 실행했습니다. Concepts, std::span, std::bit_cast, std::source_location, std::optional, std::variant, 컴파일 시간 CRC 테이블 생성, constexpr, consteval 및 constinit을 포함한 기능들은 Cortex-M 타깃에서 성공적으로 컴파일되고 실행되었습니다. 또한 CMake 프리셋을 사용한 표준 CMake 기반 워크플로우를 통해 동일한 애플리케이션을 빌드함으로써, C++20을 활용한 현대적인 임베디드 개발의 실제 사례를 보여주었습니다.
직접 시도해 보시겠습니까? 소스 코드, CMakeLists.txt 및 CMakePresets.json을 포함한 전체 예제 프로젝트는 여기에서 확인하실 수 있습니다.

그림 1. CMake 프로젝트에서 가져온 후 IAR Embedded Workbench for Arm에서 실행 중인 임베디드 C++20 데모. 이 예제는 Libc++가 활성화된 IAR LLVM C/C++ 컴파일러를 사용하여 Cortex-M 타깃에서 최신 C++20 기능이 성공적으로 실행되는 모습을 보여줍니다.
기술 리더들에게 좋은 소식은 도입이 ‘전부 아니면 전무’일 필요는 없다는 점입니다. 기존 C 및 구형 C++ 코드는 새로운 C++20 코드와 함께 계속 컴파일되므로, 팀은 기존 제품을 계속 출시하는 동시에 새로운 드라이버나 구성 요소부터 시작하여 모듈별로 새로운 기능을 도입할 수 있습니다.
비즈니스적 이점은 시간이 지남에 따라 점차 쌓입니다. 컨셉트(concepts), 스팬(span), 콘스엑스프(constexpr)를 사용하는 코드는 검토하기 쉽고 오용하기 어려우므로, 수십 년에 달하는 제품 수명 주기 전반에 걸쳐 유지보수 비용을 절감해 줍니다. 또한 인력 측면에서도 도움이 됩니다. 이 분야에 새로 입문하는 엔지니어들은 C++11부터 C++을 배우기 시작하며, 그들이 익숙한 C++과 유사한 코드베이스라면 합류하고 기여하기가 더 수월합니다.
IAR 플랫폼은 컴파일러, 디버거, 정적 분석 도구를 하나의 워크플로우로 통합하므로, 이러한 개선 사항은 도구를 하나씩 차례로 적용하는 것이 아니라 전체 툴체인에 한 번에 반영됩니다.
프로젝트에 C++20이 적합한지 판단하는 가장 빠른 방법은 직접 작성한 코드를 C++20으로 컴파일하여 결과를 확인하는 것입니다.
IAR Embedded Workbench의 무료 평가판을시작하거나, 대화형 IAR Embedded Development Platform 데모를 통해 전체 워크플로우를 살펴보세요.