메아리 저널

printf와 친구들 (2)

일단 안 보신 분께서는 앞 글부터 먼저 보고 오시길.

C++ + Boost

Boost 라이브러리는 C++ 표준 클래스 라이브러리와 호환되는 다양한 기능들을 제공하는 범용 라이브러리이다. (Boost의 영향력이 얼마나 큰가 하면 다음 C++ 표준에 적어도 10개 이상의 Boost 라이브러리가 포함될 예정이다.) iostream이 형 검사는 확실히 하는 데다가 customize가 가능함에도 불구하고 printf는 몇 글자의 포맷 문자열만 가지고 웬만한 일을 다 할 수 있다는 점에서 여전히 괜찮은 물건이었다. 한 번 다음 예시를 비교해 보시라.

// using printf
#include <stdio.h>
#include <math.h>
printf("%-30.10f\n", M_PI);

// using iostream
#include <iostream>
#include <iomanip>
#include <cmath>
using namespace std;
cout << setw(30) << setprecision(10) << left << M_PI << endl;

...출력하는 줄만 비교하면 printf가 훨씬 간단하다!

그렇게 해서 C++에서 printf랑 비슷하게 쓸 수 있는 걸 찾다가 만들어진 것이 Boost.Format 라이브러리이다. C++에서는 가변 인자에서 형 체크를 할 방법이 존재하지 않으므로 대신 % 연산자를 남용-_-하게 된다. 위의 코드를 다시 써 보자.

// using iostream with boost.format
#include <iostream>
#include "boost/format.hpp"
#include <cmath>
using namespace std;
cout << boost::format("%-30.10f") % M_PI << endl;

printf와 거의 비슷한 문법으로 똑같은 효과를 볼 수 있다! 게다가 이 라이브러리는 iostream을 사용하기 때문에 iostream 용으로만 연산자 오버로딩을 하면 자동으로 customize가 되는 것이다.

Boost.Format은 boost::format이라는 포매팅 클래스에 포매팅 문자열을 넣어서 인스턴스를 만든 뒤, % 연산자로 순서대로 인자를 집어 넣고 그 결과를 문자열로 변환하는 식으로 작동한다. (str 메소드로 직접 문자열을 받거나 앞과 같이 암시적으로 변환할 수 있다.) 지원하는 포매팅 문자열은 기본적으로 POSIX printf를 따르지만 다음과 같은 몇 가지 확장이 들어 갔다.

  • %d, %x와 같은 "명시적인" 형을 적어 주지 않고 주어진 인자의 형에 맞춰서 동작할 수 있는 포매팅 문법이 있다. 예를 들어서 %40d 대신 %|40|을 쓰면 그 자리에 뭐가 들어 와도 출력 결과는 40칸을 기준으로 정렬된다.
  • -# 등이 포함되는 옵션에 다른 정렬 방법인 =_가 추가되었다.
  • 무조건 문자열로 변환하는 경우 %n$s 대신에 %n%를 쓸 수 있다.
  • 문자열 안에서 절대 위치를 지정할 수 있는 %nt(지정한 위치 n까지 기본 채움 문자로 채움)와 %nTX(지정한 위치 n까지 X 문자로 채움)이 생겼다. 즉 %d%|30t|%d라고 하면 둘째 숫자는 적어도 문자열에서 30번째 칸 뒤에 출력된다.

아깝게도 아직 %*d와 같은 문법을 제공하지는 않지만 C++에서 printf 같은 걸 써 보고 싶다면 좋은 선택이 될 것이다.

Perl

이제 C와 C++를 벗어 나서 다른 언어를 찾아 보자. 일단 당장 생각나는 것이 C랑 살짝 비슷한 Perl이 있겠다. (Perl6은 잘 모르겠으므로 버전 5 얘기를 하겠음) perldoc를 찾아 보면 printf, sprintf 같은 함수를 볼 수 있는데, POSIX printf랑 비슷하면서 언어의 특성을 반영하는 몇몇 옵션이 추가된 것을 알 수 있다. (사실 printf 형태의 포매팅을 지원하는 대부분의 언어들은 이런 식으로 언어의 특성만 반영하는 경우가 많다.) 요약하자면,

  • 2진수로 출력하는 %b 옵션이 있다.
  • 문자열을 정수들의 배열로 보고 출력하는 %v 옵션이 있다. 예를 들어서 printf "version %vd", "\x05\x08\x08"은 "version 5.8.8"을 출력한다. v 앞에 *를 놓으면 숫자들 사이에 점(.) 대신에 출력할 문자열 앞에 나오는 문자열을 출력한다.

아, 그리고 이 시점부터는 형 검사 때문에 두려워 할 필요 없다. Perl을 포함해서, 앞으로 설명할 언어들은 모두 가변 인자를 아주 깔끔하게 지원하니까 말이다.

D

D는 C와 C++에서 단점들을 분석해서 새로 만든 프로그래밍 언어이다. (이 언어를 만든 Digital Mars는 사실 원래 C/C++ 컴파일러를 만드는 곳이었다.) 사람마다 견해차가 날 수 있겠지만 개인적으로는 C++보다 훨씬 낫다고 생각한다. 궁금하신 분은 언어 명세를 구경하고 오시면 좋겠다. 여기에 대해서는 나중에 자세히 소개할 기회가 있을 것이다.

D는 기본적으로 C++의 클래스 라이브러리를 사용하지 않고 (애초에 ABI가 서로 다름) C의 표준 라이브러리를 옵션으로 지원한다. printf의 경우 std.c.stdio.printf라는 이름을 쓰는데, 뭐 이건 C 함수니까 구현체에 달린 거라서 별 특징은 없다. 한 가지 염두에 둘 것이 있다면 D의 문자열은 int 형으로 된 문자열 길이를 문자 배열 바로 앞에 넣어서 문자열(char[])을 구현하기 때문에 printf("%s", "blah");라고 쓰면 바로 뻑난다는 것이다. 대신 printf("%.*s", "blah"); 식으로 써야 한다.

물론 이것만 있는 건 아니다. (어디까지나 "옵션"이다!) D의 표준 라이브러리에는 printf에 대응하는 std.stdio.writef라는 함수가 있다. 하지만 이 역시 C99의 printf랑 문법이 거의 동일하다시피 해서 (POSIX printf가 아니라...) 별 특징이 없기 때문에 여기에 대해서는 생략하기로 한다.

(근데 왜 D 얘기가 여기서 나왔지?)


...글이 너무 길어져서 3부에서 계속됩니다.

전체 글 목록은 다음과 같다.

(2010-03-25)

이 글은 본래 http://tokigun.net/blog/entry.php?blogid=62에 썼던 것을 옮겨 온 것입니다.


(rev 1d46270eb038)