Skip to main content

C++ of the Day #31 - Python의 Arbitrary Argument Lists 따라 잡기

어쩌다 보니 Python 따라 잡기 시리즈가 되어 가네요. :-)
이번엔 다음과 같은 Python 문법과 비슷하게 C++로 작성해 보겠습니다. 아직 실용적인 측면은 못 찾았습니다만 template programming 연습이다 생각해 주세요.
def printSum(i, j):
  print i, '+', j, '=', i + j    
  for p in zip(range(0, 5), range(0, 10)): # iterate for minumum range of two
    printSum(*p)

# output
# 0 + 0 = 0
# 1 + 1 = 2
# 2 + 2 = 4
# 3 + 3 = 6
# 4 + 4 = 8
먼저 두개의 sequence를 받아 하나의 tuple sequence로 바꿔주는 zip 함수가 보입니다. 이때 당연하게도 입력된 sequence중에 짧은쪽의 길이만큼만 만들게 됩니다. 아래 코드를 보면 쉽게 이해됩니다.
>>> zip(range(0, 3), range(0, 6))
[(0, 0), (1, 1), (2, 2)]
다음으로 printSum 함수는 두개의 인자를 받는데 for loop안에서는 두개의 인자를 넘기는 대신 arbitrary arguments lists 문법(*)으로 tuple을 인자로 넘기고 있습니다. 자동으로 이 tuple의 인자들이 함수의 인자로 펼쳐 집니다.

이번 글에서는 C++로 위의 코드를 다음과 같이 작성할 수 있도록 클래스와 함수들을 만들어 보겠습니다.
template <class T>
void printSum(T t1, T t2)
{
  std::cout << t1 << " + " << t2 << " = " << t1 + t2 << "n";
}

int range[] = { 0, 1, 2, 3, 4 };
int const N = sizeof(range) / sizeof(range[0]);

zip_iterator<int*, int*> first(range, range); // begin(), begin()
zip_iterator<int*, int*> last(range + N, range + N); // end(), end()

for (; first != last; ++first) {
  call_aal(printSum<int>, *first); // call printSum(int, int) with tuple<int, int>
}
그럼 먼저 zip_iterator를 만들어 보겠습니다. Boost.Iterator 라이브러리는 이미 이전 글에서 충분히 소개한 내용이므로 바로 구현을 보여드리겠습니다. ((C++ of the Day #25 - Boost::Iterator 라이브러리 사용하기 #1
C++ of the Day #25 - Boost::Iterator 라이브러리 사용하기 #2
C++ of the Day #25 - Boost::Iterator 라이브러리 사용하기 #3
C++ of the Day #28 - random_number_iterator 구현
))
template <class Iter1, class Iter2>
class zip_iterator : public boost::iterator_facade<
  zip_iterator<Iter1, Iter2>,
  boost::tuple<
    typename boost::iterator_value<iter1>::type,
    typename boost::iterator_value<iter2>::type
  >,
  boost::forward_traversal_tag
>
{
  typedef typename boost::iterator_value<iter1>::type value_type1;
  typedef typename boost::iterator_value<iter2>::type value_type2;
  typedef boost::tuple<value_type1, value_type2> value_type;
public:
  explicit zip_iterator(Iter1 iter1, Iter2 iter2)
    : i1_(iter1), i2_(iter2) {}

private:
  friend class boost::iterator_core_access;

  void increment() { ++i1_; ++i2_; }

  bool equal(zip_iterator const& other) const {
    return i1_ == other.i1_ || i2_ == other.i2_; // it checks using OR
    // for boundary
    // safety
  }

  value_type& dereference() const {
    return ret_ = value_type(*i1_, *i2_); // it uses dummy mutable
    // data because of reference
    // return type
  }

  Iter1 i1_;
  Iter2 i2_;
  mutable value_type ret_;
};

template <class Iter1, class Iter2>
zip_iterator<Iter1, Iter2> make_zip_iterator(Iter1 i1, Iter2 i2) // helper
{
  return zip_iterator<Iter1, Iter2>(i1, i2);
}
여기서 만든 zip_iterator는 두개의 sequence만을 받도록 되어 있으나 쉽게 세개 이상의 sequence를 받는 zip_iterator도 만들 수 있습니다. 물론 Boost.Preprocessor 라이브러리를 가지고 말이죠. :-) ((Boost.Preprocessor에 관한 내용은 C++ of the Day #30 - Python의 map 함수 따라 잡기에서 볼 수 있습니다.))

다음으로 call_aal(F, T) 함수를 구현해 보겠습니다. 여기서 F는 호출할 함수이고 T는 호출에 사용될 인자를 element로 가지고 있는 tuple입니다. F가 두개의 인자를 갖는 함수라면 T는 두개의 element를 가지는 tuple이어야 합니다.

먼저 각 tuple내의 인자의 개수마다 template specialization이 필요합니다. 하지만 이를 call_aal 함수에서 바로 처리할 수는 없죠. 따라서 인자의 개수에 따른 template specialization을 제공할 클래스가 필요합니다. 함수는 template specialization을 사용할 수 없기 때문에 클래스를 사용합니다.

먼저 다음과 같이 main template을 선언합니다. 세번째 template 인자로 tuple의 개수가 들어가게 됩니다.
template <class F, class T, unsigned int S>
struct call_aal_impl;
다음으로 각 개수에 맞는 specialization을 구현해 줍니다. 좀 무식한 방법이긴 합니다만 가장 직관적이죠. 물론 Boost.Preprocessor 라이브러리를 사용하면 쉽게 큰 개수까지 확장이 가능합니다.
template <class F, class T>
struct call_aal_impl<F, T, 0>
{
  static void do_call(F func, T t) {
    func();
  }
};

template <class F, class T>
struct call_aal_impl<F, T, 1>
{
  static void do_call(F func, T t) {
    func(boost::get<0>(t));
  }
};

template <class F, class T>
struct call_aal_impl<F, T, 2>
{
  static void do_call(F func, T t) {
    func(boost::get<0>(t), boost::get<1>(t));
  }
};
이 call_aal_impl을 사용하여 call_aal을 만드는 것은 간단한 일이죠. 다음과 같이 간단하게 tuple의 element 개수를 가지고 call_aal_impl 클래스를 선택한 후 do_call 함수를 호출해주면 됩니다.
template <class F, class T>
void call_aal(F func, T t)
{
  unsigned int const L = boost::tuples::length<t>::value;
  typedef call_aal_impl Impl;

  Impl::do_call(func, t);
}
((boost 라이브러리에서는 tuple의 element 개수를 구하기 위한 meta-function으로 tuples::length::value를 사용합니다만 tr1에서는 tuple_size::value라는 이름으로 제공됩니다.))

왠지 설명이 좀 짧은 듯한 느낌이 있었습니다만 그다지 어려운 내용이 아니므로 쉽게 코드를 읽으셨으리라 생각합니다. boost::bind와 같이 좀더 범용적으로, 즉 tuple로도 호출 가능하고 일반 인자를 가지고도 호출 가능하도록 만들수도 있을 것 같습니다만 이렇게 만들 경우 인자로 실제 tuple을 사용하는 함수의 경우 혼돈이 있을 수 있어 call_aal 함수는 arbitrary arugements lists로만 호출되도록 놔두었습니다.

암튼 다음엔 뭔가 좀더 실용적인 내용을 찾아 만들어 봐야겠네요. ;-)

Comments

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...