Skip to main content

Posts

Showing posts from August, 2006

C++ of the Day #27 - Boost::Iterator 라이브러리 사용하기 #3

이전 글에서 만들어 본 sv_iterator는 iterator_facade가 필요로 하는 함수들을 구현하여 완성할 수 있었습니다. 하지만 이 중 dereference 를 제외한 나머지 함수들은 단순히 모든 작업을 내부적으로 사용한 vector<C_iter>::const_iterator에 forwarding 하고 있습니다. 그렇다면 이런 대부분의 단순 forwarding 함수들은 작성하지 않아도 되지 않을까 생각할 수 있으며 이런 생각이 반영되어 구현된 클래스가 바로 iterator_adaptor입니다. 물론 Adaptor design pattern이겠죠? :-) 말 그대로 약간만 다른 기존의 iterator 타입을 새로운 iterator 타입으로 adapt 시켜줍니다. 그럼 먼저 iterator_adaptor의 template 인자에 대해 살펴 보겠습니다. template < class Derived , class Base , class Value = use_default , class CategoryOrTraversal = use_default , class Reference = use_default , class Difference = use_default > class iterator_adaptor 여기서 중요한 인자는 Base로 바로 iterator를 만들기 위해 내부적으로 사용되는 iterator 타입을 지정합니다. Base 뒤의 인자들은 모두 use_default로 Base로 지정한 타입에서 필요한 타입을 얻어내서 사용합니다. 하지만 특정 인자를 Base의 것과 다르게 사용하고 싶다면 명시적으로 지정해야 합니다. 예를 들어 list<int*>의 iterator 타입을 가지고 value_type을 pointer가 아닌 value로 사용할 수 있는 iterator를 만들고 싶다면 다음과 같이 선언하면 됩니다. class xx_...

C++ of the Day #26 - Boost::Iterator 라이브러리 사용하기 #2

자. 그럼 지난 번 글에 이어 boost::iterator 라이브러리를 사용하여 sorted_view의 iterator를 구현해 보겠습니다. 먼저 sorted_view의 내부 구현에는 vector가 사용되었으므로 sorted_view의 iterator도 random access traversal iterator로 구현하겠습니다. 한가지 주의할 점은 sorted_view는 const_iterator만 제공하고 iterator는 제공하지 않는다는 점입니다. 이유는 sorting된 컨테이너의 iterator를 가지고 값을 변경하면 그 컨테이너는 더이상 sorting되지 않은 상태가 될 수 있기 때문입니다. ((std::set이나 std::map의 키가 const 타입인 이유와 같습니다.)) 이 두 가지를 염두에 두고 우리가 사용할 boost::iterator_facade 에 대해서 살펴보죠. 먼저 이름에서 Facade design pattern을 사용했다는 것을 알 수 있습니다. 즉, 여러 subsystem들의 복잡성을 좀 더 사용하기 쉬운 하나의 인터페이스로 감추어 주는 역할이라는 것이죠. iterator_facade 클래스의 선언을 보면 다음과 같습니다. template < class Derived , class Value , class CategoryOrTraversal , class Reference = Value& , class Difference = ptrdiff_t > class iterator_facade { ... 여기서 Derived는 iterator_facade를 상속받는 iterator 클래스 자신입니다. ((즉, iterator_facade는 CRTP를 사용합니다.)) Value는 iterator가 사용할 value_type이고 CategoryOrTraversal은 이름 그대로 iterator가 지원하기를 원하는 category를 지정합니다. 나머지 Reference와 Difference는 이...

C++ of the Day #25 - Boost::Iterator 라이브러리 사용하기 #1

이번 시리즈에선 boost::iterator 라이브러리 를 소개합니다. 만약 stl에서 제공하지 않는 컨테이너를 만들 필요가 있다면 이 컨테이너에서 stl과 호환되는 iterator를 제공하는 것이 컨테이너의 사용자들에겐 큰 도움이 됩니다. 가장 간단한 이유는 stl의 알고리즘들을 그대로 사용할 수 있기 때문이죠. 하지만 이런 iterator 클래스를 만드는 것이 만만치 않은 작업인데 이를 도와주는 것이 바로 boost::iterator 라이브러리입니다. ((boost::iterator에는 이외에도 새로운 concept의 iterator_traits등 다른 목적들도 있습니다.)) 이번 글을 위해 만들어 볼 컨테이너가 하나 필요한데 여기서는 실제 컨테이너에 대해 sorting된 view를 제공하는 sorted_view 컨테이너를 설계해 보겠습니다. 실제로 copy에 들어가는 비용이 높은 element를 가진 컨테이너를 sorting하는 것은 성능에 문제가 될 수 있습니다. 이 컨테이너의 iterator만을 가지고 따로 컨테이너를 만들어 sorting을 하는 방법을 사용하면 실제 copy는 iterator에 대해서만 일어나므로 이 문제를 해결할 수 있습니다. 문제는 이 iterator를 가지고 만든 컨테이너의 사용법이 원래 컨테이너와 달라진다는 것입니다. 그럼 시작해 보실까요? 완성된 sorted_view의 사용법은 다음과 같습니다. using namespace std; using namespace boost::lambda; list<int> li; li.push_back(3); li.push_back(0); li.push_back(1); li.push_back(4); li.push_back(2); for_each(li.begin(), li.end(), cout ::iterator> sv_t; // (1) sv_t sv1(li.begin(), li.end()); for_each(sv1.begin(), sv1.end(), c...

C++ of the Day #24 - 다시 ADL

Question 전에도 C++ of the Day #7 - ADL (Argument Dependent Lookup) 글에서 ADL을 다룬 적이 있는데 이번엔 좀 더 난이도 있는 버전입니다. (그리고 더 자세한 설명이 있는 버전입니다. ;-) ) 내용은 뉴스그룹에서 가져왔습니다. (( VC8 compiler behavior? )) template ostream& operator & v) // (1) { // ... } namespace formatter { template struct bracketed { bracketed(const T& t) : value(t) {} T value; }; template ostream& operator & v) // (2) { return os } }; int main() { using formatter::bracketed; int n = 21014; vector v; cout (n) //cout >(v) } 위와 같은 코드에서 질문은. 왜 (4)번 라인은 컴파일이 될까? 왜 (5)번 라인은 컴파일이 되지 않을까? 답을 보시기 전에 한번 생각해 보세요. Answer 먼저 잠깐 ADL 규칙에서 후보 함수들을 찾아내는 규칙에 대해 간단히 살펴 보면 다음과 같습니다. 호출되는 함수의 각 argument가 존재하는 class나 namespace내의 같은 이름의 함수들 일반적인 name lookup 방법에 의해 찾아진 같은 이름의 함수들 ((일반적이라고 표현한 이 방법은 표준 문서에서 말하는 unqualified name lookup입니다.)) 위의 규칙에 대해서는 표준 문서에 좀 더 자세히 정의되어 있으나 일단 이렇게만 알고 있어도 문제를 해결하는데 도움이 됩니다. 일단 (4)번 라인부터 살펴 보겠습니다. 먼저 (4)번 라인의 앞부분, 즉 operator 여기서 cout argument는 std::ostr...

C++ of the Day #23 - boost::mpl을 사용한 log_n, power_n 구현

Question 바로 이전 글인 C++ of the Day #22 - MemPool 클래스 개선 (switch-case문 제거) 을 보면 log_2와 power_2 meta-function을 다음과 같이 구현하여 사용하고 있음을 알 수 있습니다. 각 함수는 main-template과 end-condition을 위한 template specialization으로 구성되어 있습니다. template <int N> struct log_2 { BOOST_STATIC_ASSERT(N % 2 == 0); enum { value = 1 + log_2<n / 2>::value }; }; template <> struct log_2<2> { enum { value = 1 }; }; template <std::size_t N> struct power_2 { enum { value = 2 * power_2<n - 1>::value }; }; template <> struct power_2<0> { enum { value = 1 }; }; 이를 boost::mpl 라이브러리를 사용하여 구현하려면 어떻게 해야 할까요? 위의 코드에 비해 얻을 수 있는 장점은 무엇일까요? Answer 먼저 power_2를 구현하기 위해 좀 더 범용적인 power_n을 mpl 라이브러리를 사용하여 구현해 보겠습니다. 먼저 n p 를 구하는 power_n 함수의 대략적인 구조는 다음과 같습니다. template <class N, class P> struct power_n : ?? { BOOST_STATIC_ASSERT(P::value >= 0); }; 이 power_n이 동작하기 위해서는 ?? 부분을 이용하여 N * power_n<N, P-1> 의 형식으로 P 항이 0 이 될때까지 recursive하게 호출되어야 ...

C++ of the Day #22 - MemPool 클래스 개선 (switch-case문 제거)

(까막님의 downcast overloading 글에서 아이디어를 얻어 작성하였습니다.) 이전에 MemPool 클래스를 작성할 때 마음엔 안들지만 방법을 찾지 못해 그냥 두었던 부분이 하나 있었습니다. 실제 코드를 읽기 쉽게 간단히 다시 써보았습니다. ((log_2와 power_2는 다음과 같이 간단히 구현되어 있습니다. template <int n> struct log_2 { BOOST_STATIC_ASSERT(N % 2 == 0); enum { value = 1 + log_2<n / 2>::value }; }; template <> struct log_2<2> { enum { value = 1 }; }; template <std::size_t n> struct power_2 { enum { value = 2 * power_2<n - 1>::value }; }; template <> struct power_2<0> { enum { value = 1 }; }; )) struct Base { }; template <int n> class Impl : public Base { }; template <int max_size, int min_size> class Allocator { public: static Base* get_impl(int index) { const int POOL_CNT = log_2<max_size, min_size>::value + 1; static Base* impl_[POOL_CNT] = { 0 }; Base* ret = impl_[index]; if (ret == 0) { switch (index) { case 0: impl_[index] = new Impl<min_siz...

C++ of the Day #21 - multi key sorting

Question 이번 문제는 sorting에 관한 내용입니다. 먼저 아래 코드는 Data의 vector를 sorting하는 코드로 Data의 operator<()을 사용하고 있습니다. using namespace std; using namespace boost::lambda; struct Data { string node; int id; Data(const string& n, int i) : node(n), id(i) {} }; bool operator<(const Data& lhs, const Data& rhs) { return lhs.node < rhs.node; } ostream& operator<<(ostream& os, const Data& data) { os << setw(5) << left << data.node << " : " << setw(2) << right << data.id; return os; } int main() { vector<data> vs; vs.push_back(Data("CAN2", 2)); vs.push_back(Data("CAN10", 13)); vs.push_back(Data("CAN10", 10)); vs.push_back(Data("CAN1", 1)); vs.push_back(Data("CAN11", 11)); stable_sort(vs.begin(), vs.end()); for_each(vs.begin(), vs.end(), cout << _1 << "n"); } 하지만 위의 코드를 실행시키면 다음과 같이 원하는대로 출력되지 않습니다. (여기서 원하는 순서...

C++ of the Day #20 - reference parameter and others

Question 이번엔 제가 얼마전에 저질렀던 실수에 관한 문제입니다. :-| 아래 함수는 dn을 입력받아 이 값을 key로 pfx를 찾고 만약 pfx가 존재한다면 pfx + dn을 uidn으로 리턴해주는 함수입니다. ((원래 코드를 간단하게 수정한 것으로 전화 번호에 지역번호를 붙여주는 함수입니다. 예: 7791000 -> (02)7791000)) 이 getUIDigit() 함수에서 문제점을 찾아보세요. //string findPrefix(const string& dn); cp::ErrorCode getUIDigit(const char* dn, string& uidn) { string pfx = findPrefix(dn); if (pfx.empty()) { uidn = dn; } else { uidn = pfx; uidn += dn; } return cp::SUCCESS; } Answer 위의 문제만 보고 못 찾으신 분들은 아래 코드를 보면 금방 생각이 나실 겁니다. ((swap 함수를 사용하는 "create a temporary and swap" idiom을 사용하면 this와 비교하는 부분은 없어도 됩니다. 관련 링크: Exception-Safe Class Design, Part 1: Copy Assignment )) Widet& operator=(const Widget& other) { if (this == &other) return *this; ... } 바로 other 인자가 this인지 확인하는 코드입니다. 위의 함수도 이와 마찬가지로 다음과 같이 dn과 uidn 인자에 같은 객체를 넘기면 문제가 발생합니다. string dn(...); cp::ErrorCode ret = getUIDigit(dn.c_str(), dn); 이 경우 uidn = pfx; 라인에서 dn이 가리키고 있던 영역은 invalidate됩니다....

The C++ Standard Library Extensions: A Tutorial and Reference 판매 시작

Pete Becker의 tr1에 관한 최초의 서적이 될 The C++ Standard Library Extensions: A Tutorial and Reference 가 드디어 판매를 시작했네요. 현재 아마존에서 구입 가능한데 달러로 사자니 $59.99로 너무 비싸군요. 8-O 강컴 에서 구입 가능할때까지 기다리기로 했답니다. 여긴 57,200원이라네요. 역시 이것도 비싸지만요. :-| 같은 날 추가 강컴에 1:1문의로 글을 남겼더니 바로 처리가 되었네요. 현재 강컴에서 주문 가능합니다. :-)

The Most Important C++ Books...Ever

Effectve 시리즈로 유명한 Scott Meyers씨가 The Most Important C++ Books...Ever 라는 글을 쓰셨네요. 다음 리스트가 위의 글에서 선택된 책들입니다. The C++ Programming Language, Bjarne Stroustrup, Addison-Wesley, 1986 (1st edition), 1991 (2nd edition), 1997 (3rd edition), 2000 (special edition). Effective C++, Scott Meyers, Addison-Wesley, 1992 (1st edition), 1998 (2nd edition), 2005 (3rd edition) Design Patterns, Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides, Addison-Wesley, 1995. International Standard for C++, ISO/IEC, 1998 (1st edition), 2003 (2nd edition). Modern C++ Design, Andrei Alexandrescu, Addison-Wesley, 2001. C++ 표준 문서는 2003년 버전을 가지고 있지 않지만 나머지 책들은 모두 최신판까지 읽어본 책들이군요. 아마 C++ 공부하신 분들이라면 모두 읽어 보셨겠죠. ((EC++ 3rd edtion은 EC++ 2nd edtion과 많이 다르더군요. 읽지 않으신 분들은 한번 읽어볼만 합니다.)) 그래서 저도 저에게 중요한 다섯권을 나름대로 뽑아 보았습니다. 굳이 C++책으로 한정하지 않고 개발에 관계된 책들 중에서 골랐습니다. Inside Windows NT 1st edtion, Microsoft Press. Effective C++, Scott Meyers, Addison-Wesley, 1992 (1st edition), 1998 (2nd edition), 2005 (3rd editi...

C++ of the Day #19 - initialization/lifetimes of static class members...

이번 글도 뉴스 그룹에서 가져왔습니다. (( c.l.c.m:initialization/lifetimes of static class members... )) Question // h class Foo { public: Foo(); private: static Bar s1_; static Bar s2_; Bar m1_; Bar m2_; }; // cpp Bar Foo::s2_; Bar Foo::s1_; Foo::Foo() : m2_(), m1_() { } 위의 코드에서 Foo 클래스는 static 멤버 변수와 non-static 멤버 변수를 각각 두개씩 가지고 있습니다. cpp 파일의 내용이 위와 같다면. 각 멤버 변수의 초기화는 어떤 순서로 이루어질까요? 또한 그 이유는 무엇일까요? Answer 이 문제의 답이야 여러 책이나 글에서 보셨을 겁니다. 정답은 static 멤버의 경우 s2_, s1_의 순서로 초기화되며 이유는 static 멤버의 경우 translation unit안에 정의된 순서대로 초기화되기 때문입니다. Non-static 멤버 변수의 경우에는 m1_, m2_의 순서입니다. 이 경우는 member initialization list에 있는 순서와 상관 없이 클래스에 선언된 순서대로 초기화되기 때문이죠. 그런데 뉴스그룹에 이 문제에 대해 왜 이렇게 초기화 규칙에 일관성이 없는가라는 질문이 있었습니다. 왜 하나는 클래스안에 정의된 순서고 하나는 아니냐는 것이죠? 이 질문에 Kanze 아저씨가 짧고 명확하게 답을 해주셨습니다. 두 경우에 대해 모두 초기화 순서는 definition 순서에 따라 결정됩니다. Non-static 멤버의 정의는 클래스 정의안에 있지만 static 멤버의 정의는 translation unit에 있습니다. In both cases, order is deterimined by the order of definition. Non static members are defined in t...

이번 주는 휴가중입니다.

이번 주는 휴가중입니다만 애기들을 보느라 방콕하고 있습니다. 정말 쌍둥이 보려니 인터넷 들어와 새글은 물론 답글 달 시간도 없네요. 얼른 휴가 끝나고 회사 나가서 좀 쉬고 싶군요. :-) 이제 7개월째 접어든 저희 집 둥이들이 궁금하신 분들은 가족 홈페이지 로 놀러오세요~ ;-)