Skip to main content

The devil's in the details.

정말 오랫만에 글을 쓰네요. XMLCPP 라이브러리 만드는데 재미를 붙여서 잠시 글쓰기를 안했더니 금방 글쓰기가 귀찮아지네요. 아니면 두려워진건지도... -_-;

최근에 했던 일은 XMLCPP에 autoconf를 적용하는 것이었습니다. autoconf란 소스 코드 패키지를 다양한 Posix-like 시스템에서 build할 수 있도록 도와주는 shell script를 만들어주는 도구입니다. 보통 소스 코드로 된 패키지를 구하면 하는 configure, make, make install의 과정이 대부분 autoconf가 만들어준 것들입니다.

왠지 configure하는 패키지들은 멋있어 보여 저도 한번 써보고 싶었는데 기회가 그간 없었지요. 일단 책을 보고 따라하기 하여 뚝딱뚝딱 적용을 했습니다. 이걸 적용하는데도 꽤나 까다로웠지요. 다음은 책에 나온 예제들에는 없어 헤맨 몇가지 사항들입니다.

  • $includedir 이 아니라 $includedir/xmlcpp 디렉토리에 헤더 파일을 install 하고 싶다.

  • make dist시에 tests 디렉토리 밑의 *.xml 파일도 tar되어야 한다. 하지만 install되진 않아야 한다.

  • make check시에 boost_unittest_framework으로 작성한 unit test가 수행되어야 한다.

  • --with-iconv 옵션을 추가한다.

  • boost 라이브러리 존재 여부를 검사해야 한다.



그래도 여기까지 작업 하고 나서 configure 실행시키면 쭈욱 올라가는 check list들을 보니 뿌듯하더군요. 이제 다음 단계로 제가 코딩한 Ubuntu 환경이 아닌 다른 환경에서도 정말 동작하는지 확인하기로 했습니다.

먼저 Redhat. 정말 다를게 없어보였습니다. 같은 리눅스라 그냥 되려니 생각했는데 make test하니 에러가 와장창 나더군요. ㅜㅜ

차이점은 gcc 버전... 검색해보니 특정 버전의 libstdc++.a 에 버그가 있더군요. fstream에 있는 버그인데 이로 인해 StreamCharDecoder에서 사용한 fs.readsome() 함수가 전혀 파일을 읽지 못했던 것이었습니다. 그래서 readsome()과 peek()를 사용하던 것을 바로 read(), gcount(), clear()를 사용하는 것으로 수정해서 Redhat으로의 포팅(? 좀더 정확히는 g++ 3.2.3으로의 포팅)은 성공.

다음으로 제가 access할 수 있는 Posix-like 시스템으로는 SunOS5.8과 윈도우에 설치한 Cygwin, MinGW등이 있었습니다. 그래서 다음으로 시도한 것이 SunOS 5.8. 이건 바로 포기가 되더군요. -_-;

XMLCPP는 XML 파싱을 위해 boost의 spirit 라이브러리를 사용하는데 boost의 문서를 읽어보니 저희 SunOS에 설치되어 있는 Sun WorkShop 6 update 2에 대해 다음과 같은 note가 있더군요.

The most recent reports seem to imply that this release is “hopeless for Boost”, but patches and workarounds are welcome.


다음은 Cygwin과 MinGW들에 도전해 보았습니다만 이것도 역시 포기... 이유는... Cygwin과 MinGW에서는 wstring과 wcout이 없답니다. ㅜㅜ

이러고 보니 성공한 플랫폼은 겨우 Ubuntu와 Redhat... 거의 똑같은 리눅스 두개... -_-; 하나는 더 있어야겠다 싶어 VMWare에 Solaris10을 인스톨하고 관련 개발 툴들을 인스톨했습니다. (회사에서 눈치를 봐가며 설치하느라 사흘이나 걸렸습니다. 게다가 OS 인스톨 다하고 다른 프로그램들 다 깔아 놓으면 네트웍이 안되는 바람에 세번이나 다시 OS를 깔았답니다. 원인은 Solaris 10용 vmware-tools는 버그가 있고 아직 수정된 버전은 없다는 것. ㅜㅜ)

결국 Solaris 10에서 g++을 사용하여 configure 성공... 이때도 그냥 configure로는 되지 않았고 --with-gnu-ld를 사용해야 되더군요. (끝까지 발목을 잡는 녀석들... -_-;)

떨리는 마음으로 make... 성공! make test... ... 성공!!! 비록 같은 g++을 사용한 것이지만 정말 기쁘더군요. 이제 그만 해야지 하는 마음으로 samples 디렉토리에 있는 xmlprint 프로그램으로 tests 디렉토리에 있는 utf8test.xml 파일을 읽어 보았습니다. 이게 왠일... ascii 범위를 벗어나는 문자가 시작되는 부분까지만 출력하고 중단되어버리더군요. ㅜㅜ

첫째로 든 생각은 또 실패했다는 oTL, 둘째로는 unit test들이 부족했나? 잘못 만들어졌나?라는 생각...

여기까지가 어제까지의 상태입니다. 이런 것들을 겪다보니 제목의 "The devil's in the details"라는 말이 자연스럽게 생각나더군요. -_-

내일부터 다시 Solaris 10으로의 포팅(?)에 도전해 볼 생각입니다. 해결되면 Solaris 10의 g++과 Linux의 g++의 차이점을 알게 되겠죠? 아님 또 버그? ㅜㅜ

Comments

Popular posts from this blog

1의 개수 세기 - 해답

벌써 어제 말한 내일이 되었는데 답을 주신 분이 아무도 없어서 좀 뻘쭘하네요. :-P 그리고 어제 문제에 O(1)이라고 적었는데 엄밀히 얘기하자면 O(log 10 n)이라고 적었어야 했네요. 죄송합니다. ... 문제를 잠시 생각해보면 1~n까지의 수들 중 1의 개수를 얻기 위해서는 해당 숫자 n의 각 자리의 1의 개수가 모두 몇개나 될지를 구해서 더하면 된다는 사실을 알 수 있습니다. 예를 들어 13이라는 수를 생각해 보면 1~13까지의 수에서 1의 자리에는 1이 모두 몇개나 되는지와 10의 자리에는 모두 몇개나 되는지를 구해 이 값을 더하면 됩니다. 먼저 1의 자리를 생각해 보면 1, 11의 두 개가 있으며 10의 자리의 경우, 10, 11, 12, 13의 네 개가 있습니다. 따라서 2+4=6이라는 값을 구할 수 있습니다. 이번엔 234라는 수에서 10의 자리를 예로 들어 살펴 보겠습니다. 1~234라는 수들 중 10의 자리에 1이 들어가는 수는 10, 11, ..., 19, 110, 111, ... 119, 210, 211, ..., 219들로 모두 30개가 있음을 알 수 있습니다. 이 규칙들을 보면 해당 자리수의 1의 개수를 구하는 공식을 만들 수 있습니다. 234의 10의 자리에 해당하는 1의 개수는 ((234/100)+1)*10이 됩니다. 여기서 +1은 해당 자리수의 수가 0이 아닌 경우에만 더해집니다. 예를 들어 204라면 ((204/100)+0)*10으로 30개가 아닌 20개가 됩니다. 이런 방식으로 234의 각 자리수의 1의 개수를 구하면 1의 자리에 해당하는 1의 개수는 ((234/10)+1)*1=24개가 되고 100의 자리에 해당하는 개수는 ((234/1000)+1)*100=100이 됩니다. 이들 세 수를 모두 합하면 24+30+100=154개가 됩니다. 한가지 추가로 생각해야 할 점은 제일 큰 자리의 수가 1인 경우 위의 공식이 아닌 다른 공식이 필요하다는 점입니다. 예를 들어 123에서 100의 자리에 해당하는 1의 개수는 ((123/1

std::map에 insert하기

얼마전 회사 동료가 refactoring한 코드를 열심히 revert하고 있어서 물어보니 다음과 같은 문제였습니다. 원래 코드와 refactoring한 코드는 다음과 같더군요. nvp[name] = value; // original code nvp.insert(make_pair(name, value)); // refactored 아시겠지만 위의 두 라인은 전혀 다른 기능을 하죠. C++03에 보면 각각 다음과 같이 설명되어 있습니다. 23.1.2/7 Associative containers a_uniq.insert(t): pair<iterator, bool> inserts t if and only if there is no element in the container with key equivalent to the key of t. The bool component of the returned pair indicates whether the insertion takes place and the iterator component of the pair points to the element with key equivalent to the key of t. 23.3.1.2/1 map element access [lib.map.access] T& operator[](const key_type& x); Returns: (*((insert(make_pair(x, T()))).first)).second. 원래 코드는 매번 새 값으로 이전 값을 overwrite했지만 새 코드는 이전에 키가 존재하면 새값으로 overwrite하지 않습니다. 따라서 원래 기능이 제대로 동작하지 않게 된것이죠. 그래서 물어봤죠. "왜 이렇게 했어?" "insert가 성능이 더 좋다 그래서 했지." :-? 사실 Fowler 아저씨는 Refactoring 책에서 refactoring은 성능을 optimizing하기 위한 것이 아니다라

C++ of the Day #9 - Boost.Python 사용하기 #1

Python 은 가장 인기있는 interpret 언어중의 하나입니다. Python의 장점 중 하나는 C/C++ 모듈과 쉽게 연동할 수 있다는 점입니다. 물론 손으로 일일히 wrapper를 만드는 것은 손이 많이 가고 에러를 만들수 있는 작업이나 SWIG 등과 같은 도구를 사용하면 쉽게 python 모듈을 만들 수 있습니다. Boost.Python 은 이런 SWIG와 같이 python 모듈을 쉽게 만들 수 있도록 도와주는 라이브러리로 순수 C++만을 사용한다는 점이 SWIG와 다른 점입니다. 그리고 개인적으로는 Boost 라이브러리에 포함되어 있는 것들이 왠지 좀 더 믿음직스러워서... :-) 이번 글에서는 Boost.Python 문서에 나와 있는 예제 를 가지고 간단하게 python 모듈을 만드는 방법에 대해서 알아보겠습니다. Requirements 리눅스 이 글에서는 리눅스 환경에서의 사용 방법을 설명한다. Boost.Python 라이브러리 (1.33.1) Boost 라이브러리를 다운로드받아 아래와 유사한 명령으로 라이브러리를 빌드한다. bjam -sTOOLS=gcc -with-python install bjam의 --prefix 옵션으로 라이브러리가 설치될 위치를 변경할 수 있다. Python 라이브러리 (2.4.3) Python을 다운로드 받아 빌드하여 설치한다. 위의 경우와 유사하게 configure의 --prefix 옵션으로 설치될 위치를 변경할 수 있다. Write C++ Code 다음과 같이 코드를 작성한다. // greet.cpp #include <stdexcept> char const* greet(unsigned x) { static char const* const msgs[] = { "hello", "Boost.Python", "world!" }; if (x > 2) throw std::range_error("