본문 바로가기

Library/C/C++

C++는 C가 아니다: 이식성

많은 사람들이 알고 있겠지만, C++ 언어와 C 언어는 많은 부분을 공유하지만, C++ 언어는 공식적으로 C 언어를 계승하는 것은 아니다. C++ 표준화 의원회와 C 표준화 의원회는 서로 긴밀히 협조하기는 하지만, 같은 개발 공동체가 관리하고 있지 않다. 물론, C#과 C의 관계보다는, C++과 C의 관계가 훨씬 밀접하다. 그러나, C++의 복잡한 기능 때문에 고질적으로 컴파일러의 이식성 문제가 따라다니며, 어떤 플랫폼에서도 C++로 작성된 코드를 직접 바인딩하는 것은 쉬운 문제가 아니다.

 

안드로이드는 NDK를 통해 C/C++를 사용하여 시스템 내부로 접근할 수 있지만, NDK는 성능이 중요한 영역에서 보조적인 수단일 뿐이며, 프레임워크 개발 언어인 자바를 대체할 수 없다. 더구나, 안드로이드가 사용하는 JNI는 C 인터페이스에 기반하고 있기 때문에, C++의 고유한 특징을 사용한 인터페이스를 해석할 수 없다. 따라서, 네이티브 영역에서 안드로이드와 데이터를 주고 받아야 한다면, C++ 인터페이스는 결국 C 인터페이스로 변환되어야 한다. 단순히 복사되는 것이 아닌, 참조를 유지할 필요가 있는 데이터라면, C++ 인터페이스를 단순히 C 인터페이스로 변환하여 데이터를 주고 받는 것은 해당 C++ 구현물의 디자인 근본 원칙을 무너뜨릴 수 있다. 예를 들어, C++ 구현에서 스마트 포인터를 사용하고 있고, 스마트 포인터가 소유하고 있는 리소스를 안드로이드와 주고 받아야 하는 경우, 스마트 포인터의 인터페이스는 JNI를 통해 표현될 수 없기 때문에, 스마트 포인터 자체를 안드로이드에서 사용할 방법은 없다. 만약, 스마트 포인터의 리소스를 C++ 관리 영역 밖으로 노출시킨다면, 더 이상 스마트 포인터를 사용하는 의미가 없으며, C++가 끼어든 이 디자인은 문제의 복잡도만 증가시킬 뿐이다.

 

iOS는 Objective-C에 기반하고 있지만, Objective-C는 C에 메타 클래스 기반의 런타임을 추가하여 OOP를 구현했기 때문에, 당연히 C 코드를 직접 컴파일하거나 C 라이브러리와 링크 할 수 있다. 특히, iOS의 LLVM는 최신 C++ 표준에 대한 지원을 늘려가고 있고, LLVM 외에 gcc도 구동할 수 있기 때문에, C++ 활용도는 안드로이드보다 확실히 낫다. 같은 C라는 조상을 두고 있는 Objective-C와 C++이지만, Objective-C는 런타임 시점에서 동적 바인딩의 힘이 극대화된 언어이며, 반대로 C++는 컴파일 시점에서 메타 프로그래밍의 힘이 극대화되는 추세에 있는 언어다. 양극단에 위치한 두 언어를 바인딩하는 것은 어렵지 않지만, Objective-C 대신 C++를 사용하여 프로그램을 디자인하고, 이 프로그램이 코코아 프레임워크와 잘 어울려 동작하게 만드는 것은 간단하지 않다. 코코아 프레임워크는 Objective-C의 강력한 동적 바인딩 능력을 크게 활용하고 있기 때문이다. 다시 말해, 디자인 단계에서 Objective-C와 C++가 중복된 기능을 제공할 경우, Objective-C가 플랫폼과의 연동 측면에서 훨씬 유리하다.

 

하드웨어에 대한 얇은 글루(glue) 레이어를 유지하려는 목적의 C와 달리, C++는 그 이상의 기능과 복잡함을 가지기 때문에 플랫폼의 프레임워크 개발 언어가 제공하는 기능과 중복될 가능성이 높다. 예를 들어, 안드로이드에서 UI를 꾸밀 때 XML 기반의 레이아웃 기능을 사용하는 것이 제일 간편하고, 자바 코드를 작성하는 것이 조금 더 복잡하다. 그러나, C++ 크로스 플랫폼 툴킷을 사용하여 UI를 표현하고자 한다면 이것은 이식성을 위한 이식성일 뿐, 아무런 이득도 없다. 그렇다면, 중복되지 않는 기능을 제공하려는 목적으로 C++를 사용하는 것은 어떠한가? 이와 같은 경우라면, 굳이 컴파일러의 이식성 문제 때문에 골머리를 앓는 C++을 사용할 필요가 없다. C++ 대신, C는 바로 이런 경우에 가장 잘 맞으며, C 컴파일러는 어느 플랫폼에서나 매우 안정적인 코드를 만들어낸다. 또, 어느 플랫폼이라도 C 라이브러리를 직접 링크할 수 있는 수단을 제공한다.

 

그렇다면, 마지막으로 C++를 프레임워크 주력 언어로 채택한 플랫폼은 어떠한가? C++를 주력 언어로 채택한 플랫폼은 윈도우를 꼽을 수 있다. 윈도우의 모든 바이너리 컴포넌트 기술은 COM에 기반하고 있고, COM의 인터페이스는 C++의 v-table을 염두에 두고 디자인되었다. 특히, 윈도우 8의 새로운 네이티브 API인 Windows Runtime(이하 WinRT)은 COM을 근간으로 .NET의 장점을 혼합한 것이며, 특히 C++을 사용할 때 최고의 성능을 보여준다. 근 10년간 강력하게 밀어붙였던 .NET보다 더 근간에 위치한 WinRT가 C++ 기반이라는 것은, 윈도우에서는 다른 어떤 플랫폼보다 C++ 코드의 이식성 문제를 고려할 필요가 없다는 것을 뜻한다. 실제로, Visual C++ 컴파일러가 지원하는 수준에 따라 어떠한 C++ 코드라도 윈도우 시스템과 직접적으로 상호 작용할 수 있다. 윈도우는 C++를 가장 적극적으로 사용하는 플랫폼이며, C++ 르네상스를 주도하는 유명한 개발자들이 대거 컴파일러 / 프레임워크 개발팀에 포함되어 있다. 여기까지 이야기를 듣는다면, 윈도우는 C++로 작성된 수 많은 코드를 직접 사용할 수 있고, 고성능 네이티브 코드를 작성할 수 있다는 두 마리 토끼를 모두 잡은 것처럼 보일지도 모르겠다. 하지만, 실상은 그렇지 않다. 구글이나 애플은 이것을 고려하지 않았기 때문에 각각 자바와 Objective-C을 채택한 것이 아니다. 문제는 C++는 지나치게 복잡하다는 점이다. 특히, 이식성 문제를 고려해야 할 정도라면 C++11 표준 준수 여부가 가장 큰 이슈일텐데, 일반 개발자들이 C++11의 잠재력을 활용하기에는 아직 시간이 더 필요하다. 안드로이드와 iOS가 플랫폼 통합 측면에서 뭔가 아쉬울지 몰라도, 최소한 COM, WinRT, CLR과 .NET을 이해하는 것보다 훨씬 간단하다. C++를 사용하여 WinRT를 활용하고자 한다면 COM에 대한 이해가 필수적이며, 병렬 / 비동기 프로그래밍에 사용한 새로운 C++11 디자인 기법들은 현 단계에서 이해하기 쉽지 않다.

 

결론적으로, 지금과 같은 상황이 계속된다면, 이식성에 있어서 C/C++라는 말은 더 이상 어울리지 않는 듯 하다. 서로 다른 철학을 구현한 플랫폼들의 춘추 전국 시대가 계속되고 있고, 이 혼란스러운 상황에서 이식성을 보장하는 것은 그나마 C 언어 뿐이다. C++는 계속 확장되는 표준 외에도 boost라는 강력한 공동체에서 끊임없이 새로운 기능을 실험하고 있기 때문에, 컴파일러의 지원 문제가 해결될 날은 요원해 보인다. C++는 C를 대체하는 것보다, 다른 4세대 언어와 경쟁하는 것으로 봐야 하며, C와 강력한 호환성을 가지는 것은 하나의 비교 우위일 뿐이다. 표준 지원 여부를 제외한다면, C++ 컴파일러는 다른 언어보다는 이식성이 좋지만, 이것은 플랫폼의 지원이 없고, 하나의 언어로 모든 시스템을 구축해야 할 때 유리할 뿐이다. 바로 이 인식이 현 시대의 C++와 C의 관계를 이해하는 출발이다.