'Language/C++'에 해당되는 글 9건

  1. 2008/04/16 귀차니스트 std::ostreambuf_iterator, std::istreambuf_iterator의 이유 (1)
  2. 2008/04/13 귀차니스트 std::auto_ptr - Smart Pointer 이지만?? (1)
  3. 2008/03/15 귀차니스트 iterator_traits - 데이터 타입에 대한 메타사용 (3)
  4. 2008/03/12 귀차니스트 더 이상 rand()는 필요없다?! - boost::random Number Library (2)
  5. 2008/03/12 귀차니스트 TR1 Array - boost 라이브러로 사용하는 방법 (1)

  저도 STL에 있어서 많은 것을 알고 있는 사람이 아니고, 아직 미숙하지만 개중에 입출력과 관련해서 유용한 iterator가 있다는 것을 알아 유용하게 사용하고 있습니다. 제가 사용하는 부분은 std::copy 알고리즘에서 std::ostream_iterator를 이용한 개체 내용의 출력부분입니다. 이를 이용한 코드는 저번에도 포스팅이 되었던 내용입니다. 그런데 이 ostream_iterator, istream_iterator에 대하여 Effective STL에서 한 가지 주제가 존재하여서 그에 대한 내용을 보려고 합니다.

iteratorbuf_iterator.cpp (Language : cpp)
  1. #include <iostream>
  2. #include <algorithm>
  3. int main( int argc, char **argv )
  4. {
  5.     std::copy( ( std::istreambuf_iterator< char >( std::cin ) ), std::istreambuf_iterator< char >(), std::ostreambuf_iterator< char >( std::cout ) );
  6.     return 0;
  7. }

  주로 사용은 이렇게 하더군요. 이게 가능한 까닭은 바로 다음과 같은 코드에 있다는 것이 중요합니다.

copy.cpp (Language : cpp)
  1. template<class _InIt, class _OutIt>
  2. inline
  3. _SCL_INSECURE_DEPRECATE
  4. _IF_NOT_CHK(_OutIt) __CLRCALL_OR_CDECL copy(_InIt _First, _InIt _Last, _OutIt _Dest)
  5. {   // copy [_First, _Last) to [_Dest, ...)
  6.     return (_Copy_opt(_CHECKED_BASE(_First), _CHECKED_BASE(_Last), _Dest,
  7.         _Iter_random(_First, _Dest), _Ptr_cat(_First, _Dest), _Range_checked_iterator_tag()));
  8. }
istreambuf_iterator.cpp (Language : cpp)
  1. template<class _Elem,
  2.     class _Traits>
  3.     class istreambuf_iterator
  4.         : public iterator<input_iterator_tag,
  5.             _Elem, typename _Traits::off_type, _Elem *, _Elem&>
  6. .
  7. .
  8. .
  9. _Elem operator*() const
  10. {   // return designated value
  11.     if (!_Got)
  12.         ((_Myt *)this)->_Peek();
  13.  #if _HAS_ITERATOR_DEBUGGING
  14.     if (_Strbuf == 0)
  15.         _DEBUG_ERROR("istreambuf_iterator is not dereferencable");
  16.  #endif /* _HAS_ITERATOR_DEBUGGING */
  17.     return (_Val);
  18. }
ostreambuf_iterator.cpp (Language : cpp)
  1. template<class _Elem,
  2.     class _Traits>
  3.     class ostreambuf_iterator
  4.         : public _Outit
  5. .
  6. .
  7. .
  8. _Myt& operator=(_Elem _Right)
  9. {   // store element and increment
  10.     if (_Strbuf == 0
  11.         || traits_type::eq_int_type(_Traits::eof(),
  12.         _Strbuf->sputc(_Right)))
  13.     _Failed = true;
  14.     return (*this);
  15. }

  Copy 알고리즘에서는 iterator이기 때문에 *연산자를 통하여 값을 읽어오게 되고, ostreambuf_iterator에 = 대입 연산자로 값을 입력하기 때문에 그렇습니다. 실제로 대충 돌아가는 루틴이 눈에 보이시죠?^^. 이 istreambuf_iterator, ostreambuf_iterator와 istream_iterator, ostream_iterator가 다른 점은 기타 점도 있겠지만 출력에 있어서 값을 읽어오고 값을 출력하는 부분이 _Strbuf 를 이용한 직접 액세스 라는 점입니다.

istream_iterator.cpp (Language : cpp)
  1. template<class _Ty,
  2.     class _Elem = char,
  3.     class _Traits = char_traits<_Elem>,
  4.     class _Diff = ptrdiff_t>
  5.     class istream_iterator
  6.         : public iterator<input_iterator_tag, _Ty, _Diff,
  7.             const _Ty *, const _Ty&>
  8. .
  9. .
  10. .
  11. const _Ty& operator*() const
  12. {   // return designated value
  13.  #if _HAS_ITERATOR_DEBUGGING
  14.     if (_Myistr == 0)
  15.     {
  16.         _DEBUG_ERROR("istream_iterator is not dereferencable");
  17.         _SCL_SECURE_OUT_OF_RANGE;
  18.     }
  19.  #else
  20.     _SCL_SECURE_VALIDATE_RANGE(_Myistr != 0);
  21.  #endif /* _HAS_ITERATOR_DEBUGGING */
  22.     return (_Myval);
  23. }
ostream_iterator.cpp (Language : cpp)
  1. template<class _Ty,
  2.     class _Elem = char,
  3.     class _Traits = char_traits<_Elem> >
  4.     class ostream_iterator
  5.         : public _Outit
  6. .
  7. .
  8. .
  9. ostream_iterator<_Ty, _Elem, _Traits>& operator=(const _Ty& _Val)
  10. {   // insert value into output stream, followed by delimiter
  11.     *_Myostr << _Val;
  12.     if (_Mydelim != 0)
  13.         *_Myostr << _Mydelim;
  14.  #if _HAS_ITERATOR_DEBUGGING
  15.     if (!*_Myostr)
  16.     {
  17.         _DEBUG_ERROR("ostream_iterator is not dereferencable");
  18.         _SCL_SECURE_OUT_OF_RANGE;
  19.     }
  20.  #else
  21.     _SCL_SECURE_VALIDATE_RANGE(*_Myostr != NULL);
  22.  #endif /* _HAS_ITERATOR_DEBUGGING */
  23.     return (*this);
  24. }

  buf_iterator와 다른 점은 <<, >> 연산자를 통하냐 통하지 않고 직접 액세스 하느냐에 따라 달렸습니다. 이 점 때문에 buf_iterator와 아닌 것의 속도 차이가 분명 존재하게 됩니다. 그래서 별 다른 문제가 존재하지 않고 buf_iterator로써 가능하다면 이 것을 사용하는 것이 속도상으로 좋다는 말이 여기서 나오는 말 일테구요.
  하지만 장점이 있다면 단점도 존재하겠죠??

Error.cpp (Language : cpp)
  1. #include <iostream>
  2. #include <algorithm>
  3. int main( int argc, char **argv )
  4. {
  5.     std::copy( ( std::istreambuf_iterator< char >( std::cin ) ), std::istreambuf_iterator< char >(), std::ostreambuf_iterator< int >( std::cout ) );
  6.     return 0;
  7. }

  이 코드를 실행해 보면 에러가 발생합니다. 그 이유는 바로 rdbuf 개체를 통한 직접 액세스를 std::cout 에서 하기 때문에 그렇습니다. 대충 이러한 오류가 발생하는 까닭은 std::cout이 다음과 같이 정의 되어있기 때문인것 같습니다.

cout.cpp (Language : cpp)
  1. __PURE_APPDOMAIN_GLOBAL extern _CRTDATA2 ostream cout;
ostream.cpp (Language : cpp)
  1. typedef basic_ostream<char, char_traits<char> > ostream;

  위와 같이 정의가 되어있는데 basic_ostream이 int 형으로 됨으로 인하여 생성되는 Class 는  typedef basic_ostream<_Elem, _Traits> ostream_type; 와 같은 형태인데. 이 형태를 int 형 buf_iterator 내부에 존재하는 클래스와 호환이 안되기 때문에 그런 것 같군요.
  정확한 이유는 한 번 제대로 따라가봐야 할 것 같은데, 일단 결론만 얘기하자면 ostream_iterator는 int 형태로 잡아주어도 출력이 가능합니다. 위에 언급한 바와 같이 <<, >> 연산자로써 std::cout << 1; 을 실행하는 것과 동일하게 처리가 된다고 보시면 되니까요.

  buf_iterator 와 아닌 것의 차이를 대충 보았는데, 간단한 몇 자의 타자의 차이임에도 불구하고 속도의 차이가 벌어질 수 있다는 점. 놓칠 수 없다고 생각되지 않나요?^^. 하나하나 살펴보다 보면 모든 부분을 정복할 수 있을 것이라 생각이 드는군요^^;

크리에이티브 커먼즈 라이센스
Creative Commons License
2008/04/16 22:33 2008/04/16 22:33

댓글을 달아 주세요

  1. 최익필 2008/07/27 21:16  댓글주소  수정/삭제  댓글쓰기

    트랙백이 가지 않아 댓글로 남겨 둡니다.^^ 입출력 스트림 라이브러리 찾덧 중에 좋은 정보가 있어서 들렸습니다.

    관련글 : http://ikpil.com/552

std::auto_ptr - Smart Pointer 이지만??

Language/C++ 2008/04/13 23:59 귀차니스트

  boost::shared_ptr 에 대해서는 이미 아시고 계실테고, 관심이 더욱 있으신 분, 혹은 이미 아시는 분들은 boost::scoped_ptr 에 대해서도 알고 계실겁니다. 그런데 C++ 에도 표준으로 원래 존재하는 Smart Pointer가 존재합니다. 그 이름은 auto_ptr<> 입니다. 어떤 것이냐구요??

autoptr.cpp (Language : cpp)
  1. #include <memory>
  2. #include <iostream>
  3. int main(int argc, char **argv)
  4. {
  5.     std::auto_ptr< int > aa( new int );
  6.     *aa = 100;
  7.     std::cout << *aa << std::endl;
  8.     return 0;
  9. }

  대충 이렇게 사용합니다. 그럼 scope를 벗어 날때 auto_ptr의 ~소멸자가 호출됨으로 인하여 해당 보관 메모리를 해제하게 됩니다. 그런데 사용은 아주 간단하게 보이지만 신경 써주어야하는 점이 몇가지 있습니다. 무엇인가 하면 new와 new [], delete, delete []의 차이점 때문이죠.

~auto_ptr.cpp (Language : cpp)
  1. ~auto_ptr()
  2. {   // destroy the object
  3.     if (_Myptr != 0)
  4.         delete _Myptr;
  5. }

  소멸자에서 사용하는 부분이 delete [] 가 아닌 delete이기 때문에 실질적으로 생성자에서 new int 가 아닌 new int[ 100 ] 과 같은 구문을 사용하였을때, 하나의 개체를 제외하고는 소멸자가 호출되지 않습니다. 그리고 할당된 개체가 int, double 등의 기본 타입이 아닌 메모리를 또 개별적으로 관리하는 개체일 경우는 더 큰 문제가 발생하게 되겠죠.
  또 하나 생길 수 있는 문제점이란 무엇일까요?? 바로 = operator overloading으로 인한 소유권 이전에 대한 문제점입니다.

auto_ptr.cpp (Language : cpp)
  1. auto_ptr<_Ty>& operator=(auto_ptr<_Ty>& _Right) _THROW0()
  2. {   // assign compatible _Right (assume pointer)
  3.     reset(_Right.release());
  4.     return (*this);
  5. }
  6. void reset(_Ty* _Ptr = 0)
  7. {   // destroy designated object and store new pointer
  8.     if (_Ptr != _Myptr && _Myptr != 0)
  9.         delete _Myptr;
  10.     _Myptr = _Ptr;
  11. }

  이렇게 구현이 되어있기 때문에 _Right에 해당하는 개체는 _Myptr이 삭제 되고, 해당 개체가 소유권을 이전 받기 때문에 문제점이 존재할 여지가 있습니다.
  어떤 부분에서냐고 물어보신다면 물론 이 것이 STL Container 과 같은 부분에서 사용이 되거나, 코드 도중 사용이 된다면 소유권이 넘어감으로 인하여 예상치 못한 점이 발생할 수 있다는 점이죠. 물론 코딩을 하는 사람치고 이런 것을 알아보지 않고 바로 사용하는 경우는 거의 없겠지만, 보통의 경우를 생각하고 코딩할 경우는 무척 문제가 될 수 있습니다. 실제로 STL Container의 경우 내부에서 대입문이 존재하는 알고리즘을 사용했을 때, 개체에 대한 메모리가 존재하지 않는 경우가 생깁니다. 이는 프로그램이 예상대로 동작하지 않는다는 소리가 됩니다. 그로인해 프로그램은 오작동 혹은 버그가 생길테구요.
  이러한 점을 보았을 때엔, 편리하지만 문제점이 존재하는 소스 양날의 검이 되지 않기 위해선 사용하기 이 전에 파악을 먼저하고 사용해야 하지 않을까 하는 생각이듭니다.

크리에이티브 커먼즈 라이센스
Creative Commons License
2008/04/13 23:59 2008/04/13 23:59

댓글을 달아 주세요

  1. kevin 2008/10/15 17:02  댓글주소  수정/삭제  댓글쓰기

    잘보고 갑니다.^^

    깔끔하게 정리하셨네요~

  예전부터 책을 읽을때 마다 보이는 것이 특질이라는 단어 였습니다. 원어 로는 traits라고 적혀져 있더군요. 그런데 C++ 이라는 언어를 총체적으로 이해를 못해서 그런지 아니면 제가 이해를 잘 못해서 그런지는 몰라도 잘 이해가 되지 않는 부분이었습니다.
  그런데 이 특질이라는 부분이 유용하게 사용되더군요. 이게 무엇인가 하면. 템플릿 특수화를 이용한 데이터 타입의 정의(?)를 통한 사용확장 이라고 생각하시면 될 것 같습니다. 적어도 저는 부족하나마 그렇게 이해했습니다.( 혹시 틀린게 있다면 댓글을 달아주세요. )

type.cpp (Language : cpp)
  1. #include <iostream>
  2. #include <vector>
  3. int main( int argc, char **argv )
  4. {
  5.     std::vector< int > aaa;
  6.     aaa.push_back( 1 );
  7.     int a = aaa.front();
  8.     std::vector< int >::reference b = aaa.front();
  9.     std::cout << a << " " << b;
  10. }

  위의 코드를 보면 integer 형태의 변수 a를 통하여 aaa container 의 front 값을 받아올 수도 있고, std::vector< int >::reference 같은 것으로 인하여 데이터를 대신 받을 수 있습니다. 여기서 하나 살펴봐야 할 부분이 std::vector< int >와 같이 선언 부분에서 typename 을 다르게 주면 당연 reference 부분도 함께 바뀌어야 합니다. 그 것이 가능한 까닭은 std::vector 선언 부분을 보시면 알 수 있습니다.

vector.cpp (Language : cpp)
  1. template<class _Ty,
  2.     class _Ax>
  3.     class vector
  4.         : public _Vector_val<_Ty, _Ax>
  5.     {   // varying size array of values
  6. public:
  7.     typedef vector<_Ty, _Ax> _Myt;
  8.     typedef _Vector_val<_Ty, _Ax> _Mybase;
  9.     typedef typename _Mybase::_Alty _Alloc;
  10.     typedef _Alloc allocator_type;
  11.     typedef typename _Alloc::size_type size_type;
  12.     typedef typename _Alloc::difference_type _Dift;
  13.     typedef _Dift difference_type;
  14.     typedef typename _Alloc::pointer _Tptr;
  15.     typedef typename _Alloc::const_pointer _Ctptr;
  16.     typedef _Tptr pointer;
  17.     typedef _Ctptr const_pointer;
  18.     typedef typename _Alloc::reference _Reft;
  19.     typedef _Reft reference;
  20.     typedef typename _Alloc::const_reference const_reference;
  21.     typedef typename _Alloc::value_type value_type;
  22. .
  23. .
  24. .
  25. 생략

  바로 template 를 통한 객체의 선언에 대한 한 벌의 해석이 만들어 질 때, typedef 를 통하여 _Alloc::reference 를 Reference 로 정의해줍니다. 그럼 밖에서는 해당 클래스 내부의 refernce 로 접근을 하거나 value_type으로 접근하면 값에 대한 접근이 가능하죠. _Alloc 이라는 부분 상세한 것 까지는 저도 잘 모르는 부분이고 계속하여 보고 있는 부분이기 때문에 곧 원인이 나올 것 같습니다.
  이런 부분에 대하여 typedef 가 이루어지는 것을 볼 수 있는데, 한 가지 의문이 생깁니다. 과연 이러한 많은 Container를 지원하는 함수를 만들어야 하고, 그 함수 안에서 변수를 임시로 담는다거나 하는 부분에 대해서는 어떻게 구현을 해야 하는가 입니다. 어려울 것 같지만 간단하게 구현할 수 있습니다.

xutility (Language : cpp)
  1. template<class _Iter>
  2.     struct iterator_traits
  3.     {   // get traits from iterator _Iter
  4.     typedef typename _Iter::iterator_category iterator_category;
  5.     typedef typename _Iter::value_type value_type;
  6.     typedef typename _Iter::difference_type difference_type;
  7.     typedef difference_type distance_type;  // retained
  8.     typedef typename _Iter::pointer pointer;
  9.     typedef typename _Iter::reference reference;
  10.     };
  11. template<class _Ty>
  12.     struct iterator_traits<_Ty *>
  13.     {   // get traits from pointer
  14.     typedef random_access_iterator_tag iterator_category;
  15.     typedef _Ty value_type;
  16.     typedef ptrdiff_t difference_type;
  17.     typedef ptrdiff_t distance_type;    // retained
  18.     typedef _Ty *pointer;
  19.     typedef _Ty& reference;
  20.     };
  21. .
  22. .
  23. .
  24. 생략

  std:: 표준 헤더에 이렇게 iterator_traits 가 선언되어있더군요. 템플릿 특수화를 사용하여 여러경우에 대하여 대처해놓은 것을 알 수 있습니다. 그럼 이 것으로 뭘 하면 될까요??

SimpleTese.cpp (Language : cpp)
  1. #include <iostream>
  2. #include <vector>
  3. template< typename T >
  4. void print( T T1 )
  5. {
  6.     std::iterator_traits< T >::value_type Value1 = *T1;
  7.     std::cout << Value1;
  8. }
  9. int main( int argc, char **argv )
  10. {
  11.     std::vector< int > aa;
  12.     aa.push_back( 1 );
  13.     print( aa.begin() );
  14. }

  코드를 위와 같이 사용했을 경우 std::vector< int > 라던가 std::vector< char > 라던가 어떠한 데이터 타입에 대한 Container에 대해서 함수가 의존성을 가지지 않게 됩니다. 함수가 더욱 확장성이 있어지는 것이죠. 전의 RLE8, LZSS 인코딩 같은 경우 그 특성상 BYTE 인코딩을 전제하고 있었으므로 char 타입으로 고정시켰습니다.
  하지만 이 경우는 좀 다르겠죠^^? 똑같은 코드를 바꿔서 치는 것만큼 어리석은 일은 없을거라고 생각하기 때문에 이런 방법들을 사용하여 뭔가 더욱 활용성 있고 확장성 있는 함수를 만들 수 있을것 같습니다.

크리에이티브 커먼즈 라이센스
Creative Commons License
2008/03/15 23:16 2008/03/15 23:16

댓글을 달아 주세요

  1. 최익필 2008/07/25 21:43  댓글주소  수정/삭제  댓글쓰기

    트랙백이 남겨지지 않아, 댓글로 남깁니다.^^ 재미있게 보았습니다.

    • 귀차니스트 2008/07/27 20:47  댓글주소  수정/삭제

      잼있으셨다니 다행이네요^^.. 블로그에 방문하니 재미있는게 엄청 많으시군요^^;;

  2. 최익필 2008/07/25 21:43  댓글주소  수정/삭제  댓글쓰기

    http://ikpil.tistory.com/540 관련글(...?)

TraditionalRandom.c (Language : cpp)
  1. #include <stdio.h>
  2. #include <time.h>
  3. #include <stdlib.h>
  4. int main( int argc, char **argv )
  5. {
  6.     srand(time(NULL) );
  7.     for( int i = 0; i < 50; ++i )   {
  8.         printf( "%d\n", rand() % 6 + 1 );
  9.     }
  10.     return 0;
  11. }

  위 구문이 익숙하시죠?? 보통 C/C++을 하시다가 어떠한 난수를 발생시키고자 할 때에 한 번쯤은 써보셨을 겁니다. 그런데 실질적으로 srand 라는 부분도 문제가 되고, % 연산자로 숫자의 범위를 만들어 주어야 하는 단점이 존재합니다. 그 뿐만 아니라 rand() 함수 자체가 short 형인 65536 까지 제한이 걸리다 보니 하고 싶은 작업에 있어서도 제한이 걸립니다. 그런데 역시라고 해야 될지 boost 에는 이런 문제를 해결 해놓은 라이브러리가 존재합니다. 바로 boost::random 입니다.

random.cpp (Language : cpp)
  1. #include <iostream>
  2. #include <boost/tr1/random.hpp>
  3. int main( int argc, char **argv )
  4. {
  5.     //boost::minstd_rand    Random1;
  6.     //boost::minstd_rand0   Random2;
  7.     //boost::mt11213b      Random3;
  8.     boost::mt19937    Random4;
  9.     boost::uniform_int<>    Six( 1, 6 );
  10.     boost::variate_generator< boost::mt19937, boost::uniform_int<> > Dice( Random4, Six );
  11.     for( int i = 0; i < 50; ++i )
  12.         std::cout << Dice() << std::endl;
  13.     return 0;
  14. }

  간단하게 보이죠?? mt19937 같은 부분은 난수발생엔진 부분이고 uniform_int 는 범위와 데이터 형태를 정의하는 부분입니다. varitate_generator 를 이용하여 해당 엔진을 typename 으로 하여 Dice 롤링을 하죠. 그 것을 바로 std::cout 을 이용해 출력하면 콘솔에서 제대로 출력됩니다. 1 ~ 6 의 숫자가 50번 동안 난발적으로 생성됩니다. 뿐만 아니라 boost::uniform_int 대신 boost::uniform_real를 사용하면 실수가 나옵니다. @0@ 많이 편하지 않나요^^. 사용할 곳이 많이 생길지도 모르겠네요.

크리에이티브 커먼즈 라이센스
Creative Commons License
2008/03/12 22:18 2008/03/12 22:18

댓글을 달아 주세요

  1. 나쁜남자 2010/08/19 16:01  댓글주소  수정/삭제  댓글쓰기

    많은 도움이 됐습니다.

  흔히 말하는 프로그래밍의 기본이라는 C/C++을 하지 않아도 어떠한 언어를 하다보면 배열이라는 것을 배우게 됩니다. 그런데 아시다시피 C/C++ 에서 기본 배열을 사용하게 되면 길이를 비롯해서 불편함 점이 많습니다. 그래서 보통 사람들은 std::vector 를 사용하는 경향이 있는데요.
  boost 에 올라와있다 TR1 표준에 포함되는 것 중에는 Array 클래스가 존재합니다. 뭐 굳이 얘길 한다면 뭐라 말할 정도로 편리해 지지는 않았지만 해당 부분에 있어서 기타 STL 알고리즘과 호환되게 하는 면이 중점적으로 다루어진것 같았습니다.

Array.cpp (Language : cpp)
  1. #include <iostream>
  2. #include <boost/tr1/array.hpp>
  3. int main( int argc, char **argv )
  4. {
  5.     boost::array<int,4> a = { 1, 2, 3 };
  6.     std::copy( a.begin(), a.end(), std::ostream_iterator< int >( std::cout, "\r\n" ) );
  7.     return 0;
  8. }

  사용법은 다음과 같습니다. 헤더는 위의 boost 라이브러리가 프로젝트 인클루드 폴더에 포함되어있다는 가정하에 컴파일이 가능하며, 이 것이 begin, end 같은 부분으로 많이 편리해졌다는 것을 알 수 있습니다. 선언에서는 데이터 형태와, 크기를 넣어주어야 하죠. 물론 실질적으로 그냥 사용할 때는 [] 리터럴을 사용하는 배열과 별 다른점을 느끼지 못할지도 모르겠지만 STL 상에서 traits 들과 결합하여 사용한다면 사용법에 있어서 많은 편리함이 생길것 같습니다.

크리에이티브 커먼즈 라이센스
Creative Commons License
2008/03/12 21:53 2008/03/12 21:53

댓글을 달아 주세요

  1. 구리구리 2009/02/23 14:47  댓글주소  수정/삭제  댓글쓰기

    잘 정리해놓으셨네요 글 좀 퍼가겠습니다.