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 << _1 << " "); cout << endl; typedef sorted_view<list<int>

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

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

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개월째 접어든 저희 집 둥이들이 궁금하신 분들은 가족 홈페이지 로 놀러오세요~ ;-)