Skip to main content

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_iterator
  : public iterator_adaptor<
      xx_iterator,
      list<int*>::iterator,
      int // not int*
    >
사실 위의 예제가 바로 이번 글에서 우리가 하려는 작업입니다. 실제 value_type은 Iter이나 이를 iterator_value<Iter>::type, 즉 *Iter 타입으로 사용하려는 것이죠.

그럼 이제 우리의 sv_iterator를 만들어 볼까요?
class sv_iterator
  : public boost::iterator_adaptor<
      sv_iterator,
      typename std::vector<c_iter>::const_iterator, // (1)
      C_value_type const // (2)
    >
{
  typedef typename sv_iterator::iterator_adaptor_ super_t; // (3)
public:
  explicit sv_iterator(typename sv_iterator::base_type p)
    : super_t(p) {} // (4)

private:
  friend class boost::iterator_core_access;

  C_value_type const& dereference() const {
    return **(this->base_reference()); // (5)
  }
};
위의 코드가 sv_iterator의 코드 전체입니다. 한결 짧아졌죠? 먼저 template 인자 부분에서 Base로 vector<C_iter>::const_iterator를 지정하면서(1) Value에는 C_value_type const를 사용했음을(2) 알 수 있습니다. 만약 Value 인자를 지정하지 않았다면 Value 타입은 C_iter const가 되어 버리겠죠?

다음으로 이전 sv_iterator에 존재했던 멤버 변수 curr_가 없어졌음을 알 수 있습니다. 이 정보는 iterator_adaptor가 Base 타입으로 가지고 있게 되며 따라서 생성자에서는 부모 클래스에 Base 타입의 값을 넣어서 이 값을 저장할 수 있습니다.(4) 여기서 super_t는 부모 클래스 타입의 typedef 입니다.(3)

마지막으로 Base iterator의 동작과 달라야 하는 dereference 함수만 구현해 주면 됩니다. 여기서는 base_reference() 함수를 사용하여 생성자에서 셋팅했던 값을 가져오고 있습니다.(5)

이것으로 sv_iterator는 끝~ :-)

너무 짧아서 섭섭한 관계로 sorted_view에 reverse_iterator를 추가해 보겠습니다. 물론 const_reverse_iterator가 되겠죠?
typedef boost::reverse_iterator<const_iterator> const_reverse_iterator;

const_reverse_iterator rbegin() const {
  return boost::make_reverse_iterator(sv_iterator(data_.end()));
}
const_reverse_iterator rend() const {
  return boost::make_reverse_iterator(sv_iterator(data_.begin()));
}
boost::make_reverse_iterator를 사용하니 이것도 너무 간단하군요. :-)

이 외에도 Boost의 iterator 문서를 보면 이외에도 다양한 특화된 adaptor들을 볼 수 있습니다.

이것으로 boost::iterator 라이브러리에 대한 소개를 마치겠습니다.

boost::multi_index_container 소개

이번에 만들어 본 sorted_view와 유사한 기능을 할 수 있는 multi_index_container라는 라이브러리가 boost에 존재합니다. 여기서는 간단히 sorted_view로 했던 작업을 multi_index_container로 하는 방법을 간단히 소개하고 마치겠습니다.
using namespace std;
using namespace boost;
using namespace boost::multi_index;

typedef multi_index_container<
  int,
  indexed_by<
    sequenced<>,
    ordered_non_unique >
  >
> int_container;

int_container ic;

int_container::nth_index<0>::type& ic0 = ic.get<0>();
int_container::nth_index<1>::type& ic1 = ic.get<1>();

ic0.push_back(3);
ic0.push_back(0);
ic0.push_back(1);
ic0.push_back(4);
ic0.push_back(2);

copy(ic0.begin(), ic0.end(), ostream_iterator<int>(cout, " "));
cout << endl;

copy(ic1.begin(), ic1.end(), ostream_iterator<int>(cout, " "));
cout << endl;

// output
// 3 0 1 4 2 
// 0 1 2 3 4 
처음 타입을 선언하는 부분이 좀 어렵지만 이 후 사용법은 일반 컨테이너와 동일합니다. 자세한 내용은 boost의 multi_index 컨테이너 라이브러리 문서에 있습니다.

Comments

  1. 전 filter_iterator를 아주 유용하게 썼던 기억이.. ^_^

    ReplyDelete
  2. 저도 boost 라이브러리를 유용하게 쓰고 싶은데 아직 저희 프로젝트에서는 boost 라이브러리를 사용하지 않고 있어서 회사에서는 사용을 거의 못하고 있습니다. ㅜㅜ
    몰래 shared_ptr와 static_assert만 가져다가 체크인해서 사용하고 있답니다. :-)

    ReplyDelete
  3. 저희는 헤더만 통째로 가져다가 쓰다가, object코드가 필요한 경우에는 해당 라이브러리만 가져다가 빌드해 쓰는 방식을 채택하고 있지요. ^^

    (결론은 bjam을 안쓴다는거.. :)

    ReplyDelete
  4. ㅋㅋ 만약 저희 회사에서 이 라이브러리를 통째로 가져다 쓰겠다고 하면 아마 그게 뭐하는 거냐서부터 시작해서 버그 있으면 누가 책임질거냐까지 설명해야 할게 너무 많을 것 같네요. 형상 관리하는 쪽에서도 시끄러울 것 같고... 그래서 몰래... :-)

    그리고 boost 전체 패키지는 개인 디렉토리에 인스톨하면서 bjam으로 전체 컴파일 해 두었습니다. 엄청 오래 걸리더군요. :-| 게다가 quota도... ㅜㅜ

    ReplyDelete
  5. 항상 설득이 가장 힘든 일인 것 같습니다. 전 요즘 교내에서도 설득하기가 힘든다는 것을 느끼고 있습니다.

    ReplyDelete
  6. 네 맞습니다. 그런데 제 경우엔 이제 거의 포기하고 아예 안하거나 몰래 하거나 둘중의 하나를 택하고 있죠. :-|
    역시 인간 관계가 가장 힘드네요.

    ReplyDelete

Post a Comment

Popular posts from this blog

CodeHighlighter plugin test page.

This post is for testing CodeHighlighter plugin which uses GeSHi as a fontifier engine. ((Those code blocks are acquired from Google Code Search .)) ((For more supported languages, go CodeHighlighter plugin or GeSHi homepage.)) C++ (<pre lang="cpp" lineno="1">) class nsScannerBufferList { public: /** * Buffer objects are directly followed by a data segment. The start * of the data segment is determined by increment the |this| pointer * by 1 unit. */ class Buffer : public PRCList { public: Buffer() { ++index_; } PHP (<pre lang="php" lineno="4">) for ($i = 0; $i $value = ord( $utf8_string[ $i ] ); if ( $value < 128 ) { // ASCII $unicode .= chr($value); } else { if ( count( $values ) == 0 ) { $num_octets = ( $value } $values[] = $value; Lisp (<pre lang="lisp">) ;;; Assignment (define-caller-pattern setq ((:star var fo...

C++ of the Day #43 - SQLite3 C++ wrapper #1

The Definitive Guide to SQLite 를 읽다가 공부 겸 해서 C++ wrapper를 만들어 보았습니다. 최대한 C++ 냄새(?)가 나도록 만들어 보았습니다. :-) ((SQLite는 복잡한 관리가 필요없이 사용가능한, 파일이나 메모리 기반의, 라이브러리로 제공되는, 약 250kb 용량의, 대부분의 SQL92문을 지원하는, open source RDB입니다.)) 이 wrapper를 사용하기 위해서는 (당연하게도!) sqlite3 와 (당연하게도?) boost 라이브러리가 필요합니다. 사용 예들을 살펴보는 것으로 설명을 대신합니다. 이번 글에서는 다음과 같은 contacts 테이블이 test.db에 존재한다고 가정합니다. CREATE TABLE contacts ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, phone TEXT NOT NULL, UNIQUE(name, phone) ); Command 먼저 test.db 파일을 사용하기 위해 다음과 같이 파일 이름을 주어 connection 객체를 생성합니다. 생성과 동시에 test.db와 연결이 이루어집니다. ((생성자외에 open() 함수를 사용할 수도 있습니다.)) sqlite3pp::connection conn("test.db"); 다음은 contacts 테이블에 정보를 추가하는 가장 간단한 방법입니다. connection 클래스에서 제공하는 execute 함수를 사용합니다. ((executef 함수를 사용하면 printf와 같은 문법을 사용하여 query문을 작성할 수 있습니다.)) conn.execute("INSERT INTO contacts (name, phone) VALUES ('user', '1234')"); 위와 동일한 작업을 parameterized query를 사용하여 할 수도 있습니다. ((step()함수가 실제 query문을 수행하는 함수입니다. ...

Textiler plugin test page

This post is for testing Textiler plugin . This plugin uses Textile engine (version 2.0.0). The sample text is come from Textile test page. (Note that the result will be vary according to your CSS options.) Supported wiki syntax Rendering result h2{color:green}. This is a title h3. This is a subhead p{color:red}. This is some text of dubious character. Isn't the use of "quotes" just lazy writing -- and theft of 'intellectual property' besides? I think the time has come to see a block quote. bq[fr]. This is a block quote. I'll admit it's not the most exciting block quote ever devised. Simple list: #{color:blue} one # two # three Multi-level list: # one ## aye ## bee ## see # two ## x ## y # three Mixed list: * Point one * Point two ## Step 1 ## Step 2 ## Step 3 * Point three ** Sub point 1 ** Sub point 2 Well, that went well. How about we insert an <a href="/" title="watch out">old-fashioned hypertext link</a>? Will the quo...