메인 컨텐츠로 가기

developerWorks 이용 약관에 동의하시는 경우 제출을 클릭하십시오. 이용 약관 보기.

developerWorks에 처음 로그인하면 developerWorks프로파일이 생성됩니다.귀하의 프로파일에서 동의하신 내용이 공개되지만 이 사항은 언제든지 변경 가능합니다. 귀하의 성명(숨김으로 체크되어 있어도 표시됩니다)과 디스플레이 이름은 게시한 컨텐츠나 사이트 엑세스시 표시됩니다.

모든 정보가 안전하게 전송되었습니다.

  • 닫기 [x]

처음 developerWorks에 로그인할 때 프로파일이 작성되므로, 이를 위해 디스플레이 이름을 선택해야 합니다. 선택하신 디스플레이 이름은 developerWorks에 게시한 컨텐츠에 표시됩니다.

3글자 이상 31글자 이하의 길이로 사용 가능합니다. dW커뮤니티 내에서는 보안상 이메일주소를 제외한 다른 이름을 지정하셔야 합니다.

developerWorks 이용 약관에 동의하시는 경우 제출을 클릭하십시오. 이용 약관 보기.

모든 정보가 안전하게 전송되었습니다.

  • 닫기 [x]

추가 Boost 유틸리티

Boost가 백그라운드에서 작동하는 방식 학습

Arpan Sen , 독립기고가, IBM
Arpan Sen은 선임 엔지니어로 전자 설계 자동화 분야의 소프트웨어 개발을 담당한다. 여러 해 동안 Solaris, SunOS, HP-UX 및 IRIX를 포함한 UNIX는 물론 Linux와 Microsoft Windows의 여러 제품과 관련된 업무를 맡아 왔다. 소프트웨어 성능 최적화 기법, 그래프 이론 및 병렬 컴퓨팅에 많은 관심을 가지고 있다. Arpan은 대학원에서 소프트웨어 시스템 분야를 전공했다. 이메일 주소는 arpansen@gmail.com이다.

요약:  Boost C++ 라이브러리를 사용하면 훌륭한 코드를 더 쉽게 쓸 수 있다. Boost 헤더 파일의 기능을 학습하고 실패한 어설션을 다루는 방법뿐 아니라, 압축된 쌍과 non-copyable 클래스와 같이 유용한 유틸리티를 검색해보자.

기사 게재일:  2011 년 12 월 28 일
난이도: 중급 원문:  보기 PDF:  A4 and Letter (60KB | 16 pages)Get Adobe® Reader®
페이지뷰:  1032 회
의견:  


소개

Boost C++ 라이브러리에는 쓰기 편하고 효과적인 코드를 쓰는 데 도움이 되는 다양한 유틸리티 기능이 있다. 이 기사에서는 압축된 쌍 및 유형 특성과 아울러, 클래스를 복사할 수 없게 만들거나 어설션 실패 시 특정 함수 콜백을 허용하도록 빠르게 도움을 주는 Boost의 일반적인 기능과 같이 유용한 몇 가지 유틸리티에 대해 살펴본다. 또한, 이 기사에서는 백그라운드에서 일어나는 마법과도 같은 일을 이해하기 위해 Boost 소스(주로 헤더 파일)를 조사해본다.

이 기사의 모든 코드는 gcc-4.3.4를 사용하여 컴파일하고 Boost 라이브러리 버전 1.45를 사용하여 테스트했다. 이 기사에서 설명하는 모든 유틸리티 기능은 알맞은 헤더(보통 boost/your_header.hpp의 형식임)를 포함한 헤더만으로 되어 있고, 컴파일 시간 중에 (GCC(GNU Compiler Collection)를 사용하는 경우에는 –I 옵션 사용) 포함 경로를 지정하는 것이 효과가 있다.

이 기사를 따라 실습하려면 템플리트에 대해 좀 더 자세히 파악할 필요가 있다. 특히, 템플리트 부분 특수화에 대한 지식이 무엇보다도 중요한 것으로 드러난다. 템플리트와 관련하여 도움말이 필요한 경우 참고자료 섹션을 참조한다.


압축된 쌍

STL(Standard Template Library)은 헤더 유틸리티에서 pair를 정의한다. pair는 이기종 유형으로 T1 유형의 한 오브젝트와 T2 유형의 다른 유형을 가진다. 목록 1은 쌍이 일반적으로 구현되는 방식을 나타낸 것이다.


목록 1. std::pair의 전형적인 구현
	
template<class _T1, class _T2>
struct pair
  {	// store a pair of values
       pair() : first(_T1()), second(_T2())
      { }     // construct from defaults
     
      pair(const _T1& _Val1, const _T2& _Val2)
          : first(_Val1), second(_Val2)
     { } 	// construct from specified values

      // … more stuff follows 
      _T1 first;	// the first stored value
      _T2 second;	// the second stored value
  };

이것은 모두 괜찮지만 정확히 최적의 상태인 것은 아니다. 예컨대, 클래스 중 하나에 멤버가 없다면 어떻게 될까? 그래도 컴파일러는 빈 클래스에 대한 공간을 할당해야 한다. 그렇지 않은가? 목록 2를 살펴보자.


목록 2. 빈 클래스의 크기가 0이 아님
	
#include <iostream>
using namespace std;
 
class A { };
 
int main()
{
  A _a;
  cout << sizeof(_a) << endl;
}

목록 2의 출력은 1이다. 컴파일러는 A 유형의 각 오브젝트에 대해 1바이트를 할당한다. 이는 유형 중 하나가 정수이고 다른 하나는 빈 클래스인 경우 해당하는 쌍의 크기는 4바이트 경계에서 오브젝트를 맞추기 위해 4(일반적으로 x86 플랫폼에서의 정수 크기) + 1(빈 오브젝트의 크기) + 오프셋(즉, 8바이트)이 된다. 목록 3에서 이 점이 증명된다.


목록 3. 쌍으로 사용되는 빈 클래스가 더 많은 메모리를 차지함
	
#include <iostream>
using namespace std;
class A { };
 
int main()
{
  A _a;
  std::pair<A, int> _b;
 
  cout << sizeof(_a) << endl; // prints 1 
  cout << sizeof(_b) << endl; // prints 8 
}

쌍 대신에, 이제는 compressed_pair.hpp 헤더에 정의된 boost::compressed_pair를 사용한다. 목록 4는 STL 쌍 대신 compressed_pair를 사용하는 코드를 나타낸 것이다.


목록 4. 메모리를 효율적으로 사용하는 실행 파일에서 compressed_pair 결과 사용
	
#include <iostream>
#include <boost/compressed_pair.hpp>
using namespace std;
class A { };

int main()
{
  A _a;
  std::pair<A, int> _b;
  boost::compressed_pair<A, int> _c;

  cout << sizeof(_a) << endl;
  cout << sizeof(_b) << endl;
  cout << sizeof(_c) << endl;
}

목록 4의 출력은 1 8 4이다.

압축된 쌍 오브젝트의 크기는 4바이트이며, 이는 std::pair 크기의 절반이다. 그렇다면 이런 메모리 감소에 숨겨진 비밀은 무엇일까? 그 해답은 바로 빈 클래스를 멤버로 포함하는 대신, Boost의 쌍 구조가 빈 클래스에서 파생된다는 점이다. 컴파일러는 그런 파생을 최적화하고, 쌍에 대해 생성되는 오브젝트는 비어 있지 않은 클래스의 크기일 뿐이다. 목록 5는 빈 기본 클래스를 최적화하는 컴파일러에서 이 점을 입증하는 코드를 나타낸 것이다.


목록 5. 빈 클래스에서의 파생을 통해 쌍 구조 최적화
	
#include <iostream>
using namespace std;
class A { };

struct modified_pair : public A { 
   int n;
}; 
 
int main()
{
  A _a;
  std::pair<A, int> _b;
  modified_pair _c; 
 
  cout << sizeof(_a) << endl;  // prints 1
  cout << sizeof(_b) << endl;  // prints 8 
  cout << sizeof(_c) << endl;  // prints 4 
}

compressed_pair 정의에 대해 Boost 헤더를 살펴보자. compressed_pair의 주요 컴포넌트는 compressed_pair_switchcompressed_pair_imp이다. 목록 6은 같은 의미의 선언을 나타낸 것이다.


목록 6. compressed_pair의 컴포넌트 상세 분석
	
template <class T1, class T2, bool IsSame, bool FirstEmpty, bool SecondEmpty>
struct compressed_pair_switch;

template <class T1, class T2, int Version> 
class compressed_pair_imp;

// Let's consider specific partial specializations
template <class T1, class T2>
struct compressed_pair_switch<T1, T2, false, true, false>
{static const int value = 1;};

template <class T1, class T2>
struct compressed_pair_switch<T1, T2, false, false, true>
{static const int value = 2;};

template <class T1, class T2>
class compressed_pair_imp<T1, T2, 1> : protected ::boost::remove_cv<T1>::type
{
  typedef T1                                                 first_type;
  typedef T2                                                 second_type;
   // … 
   private: 
      second_type second_;  // Only the second element is a class member 
}; 

compressed_pair_switchcompressed_pair_imp는 "템플리트화된" 요소이다. Boost는 이런 템플리트의 일부 부분 특수화만 정의한다. 목록 6에서는 compressed_pair_imp<T1, T2, 1>만 언급했지만, 다른 특수화가 존재한다. 두 번째 요소가 비어 있지 않고 첫 번째 요소는 비어 있을 때, (곧 알게 되겠지만) compressed_paircompressed_pair_imp<T1, T2, 2>에서 파생된다.

예상한 대로, compressed_pair_imp는 빈 클래스에서 파생된다. 이제 compressed_pair_imp에서 파생되는 compressed_pair의 정의를 살펴보자(목록 7 참조).


목록 7. compressed_pair 선언
	
template <class T1, class T2>
class compressed_pair
   : private ::boost::details::compressed_pair_imp<T1, T2,
             ::boost::details::compressed_pair_switch<
                    T1,
                    T2,
                    ::boost::is_same<typename remove_cv<T1>::type, 
                    	typename remove_cv<T2>::type>::value,
                    ::boost::is_empty<T1>::value,
                    ::boost::is_empty<T2>::value> ::value>
{ 
// … code for the class follows
};

쌍의 첫 번째 요소가 비어 있고 두 번째 요소는 비어 있지 않은 경우에는 인스턴스화된 compressed_pair 클래스에 기본 클래스로서 compressed_pair_imp<T1, T2, 1>이 있을 것이다. 기본 클래스의 세 번째 요소는 사용할 특정 템플리트 특수화를 선택하는 데 사용된다. 세 번째 요소의 값은 다음에 의해 제공된다.

struct compressed_pair_switch<T1, T2, false, true, false>::value

compressed_pair_imp<T1, T2, 1>의 정의에 주목하자. 이것은 해당 클래스가 두 번째 요소를 멤버로 가지도록 정의할 뿐이다. 마찬가지로, compressed_pair.hpp에서 정의된 것처럼 compressed_pair_imp<T1, T2, 2>는 첫 번째 요소만 멤버로 가진다.

first ( )second ( ) 메소드는 compressed_pair 클래스에서 compressed_pair_imp 클래스로 위임된다. 목록 8에는 compressed_pair_imp에서 동일한 사항에 대한 정의가 나와 있다.


목록 8. compressed_pair에 대한 첫 번째 및 두 번째 메소드
	
typedef typename call_traits<first_type>::reference        first_reference;
typedef typename call_traits<second_type>::reference   second_reference;
      
typedef typename call_traits<first_type>::const_reference      first_const_reference;
typedef typename call_traits<second_type>::const_reference second_const_reference;

first_reference            first()       {return *this;}
first_const_reference first() const {return *this;}

second_reference       second()       {return second_;}
second_const_reference second() const {return second_;}

첫 번째 요소가 빈 클래스일 때 compressed_pair_imp*this를 리턴한다.


클래스가 빈 클래스인지 어떻게 알 수 있는가?

클래스가 비어 있는지 판단하려면 T가 클래스 유형인 경우 compressed_pair가 사용한 boost/type_traits/is_empty.hpp에서 사용 가능한 boost::is_empty<T>::value만 사용하면 된다. 값이 1일 경우 클래스는 비어 있으며, 그렇지 않으면 0이 표시된다. Boost는 이를 어떻게 구현하는 것일까? 목록 9에서 기본적인 구현을 제시한다.


목록 9. 클래스가 비어 있는지 파악하기 위한 코드
	
#include <iostream>
using namespace std;
 
template <typename T>
struct is_empty<int>
{
    static const int value = 0;
};
 
class A { };
class B { double d; };
 
int main()
{
  cout << is_empty<A>::value << endl;
  cout << is_empty<B>::value << endl;
} 

목록 9에서는 컴파일러가 빈 기본 클래스에서 벗어나 최적화한다는 가정을 전제로 한다. 이제, Boost가 제공하는 is_empty와 같은 일반적인 카테고리의 유틸리티에 대해 살펴보자. 이런 유틸리티는 다음에 설명할 Boost Type Traits 라이브러리를 형성한다.


Boost Type Traits Library 학습

그렇다면 Boost Type Traits Library란 정확히 무엇일까? 라이브러리 이름부터 잘 생각해보는 것이 좋을 것 같다. 유형 특성(type traits)이란 어떤 유형에 대한 정보를 가리키는 말이다. 어떤 유형에 대해 일반적으로 파악하고 싶은 정보로는 그 유형이 기본 유형, 열거 유형, 공용체 또는 클래스/참조 유형인지 여부, 그 유형에 사소한 생성자와 소멸자가 있는지 여부 등이 포함된다. 유형 특성의 기본적인 목적은 다음 세 가지다.

  • 유형에 대한 정보 확인—예: is_pointer, is_void, is_array.
  • 두 유형 사이의 관계 테스트—예: is_same<T1, T2>, is_base_and_derived<T1, T2>.
  • 한 유형을 다른 유형으로 변환—예: remove_const<T>::typeT와 같은 유형을 작성하지만 const 규정자가 제거되어 있고, remove_cv<T>::typeT와 같은 유형을 작성하지만 constvolatile 규정자가 제거되어 있다.

Type Traits Library를 사용하려면 애플리케이션 코드에 type_traits.hpp를 포함해야 한다. 목록 10은 유형 특성이 제공하는 기능 중 몇 가지만 나타낸 것이다.


목록 10. Boost에서 제공하는 몇 가지 유형 특성
	
template <typename T>
struct is_empty;

template <typename T>
struct is_array;

template <typename T>
struct is_class;

template <typename T>
struct is_floating_point;

template <typename T>
struct is_enum;

template <typename T>
struct is_function;

그런데 Type Traits Library를 사용하려는 이유가 무엇일까? 종종 일반 라이브러리를 작성할 필요가 있고, 일부 특정 유형의 경우 일반적인 작동을 피하고 특수한 방식으로 구현하려 한다는 사실에 이 질문에 대한 답이 있다. Type Traits Library가 이런 필요성을 충족시키는 데 도움이 될 수 있다. 유형 특성을 위한 Boost 헤더의 구현을 한 기사에서 설명하기에는 너무 방대한 주제라서 이 기사에서는 이 문제를 직접적으로 깊이 다루지는 않지만, 일반적인 구현 전략에 대한 몇 가지 용도와 고찰을 간략히 다룬다. 목록 11에서는 is_array 특성에 대해 가능한 구현을 제시한다.


목록 11. 일반적인 is_array<T> 구현
	
template<class T>
struct is_array{
  static const bool value = false;
};

template<class T, std::size_t N>
struct is_array< T (&)[N] >{
  static const bool value = true;
};

위에서 보는 바와 같이, 간단히 구현할 수 있었다. 배열 유형에 대한 특수화가 도움이 된 것을 알 수 있다. 코드에서 array<T>::value를 사용하고 적절한 조치를 취하면 된다. 다만, 이것이 Boost가 구현하는 방식과 정확히 일치하는 것은 아니라는 점은 기억해두자. 유형 특성 템플리트는 True 유형 또는 False 유형에서 파생된다. 따라서 목록 11에서는 배열에 대해 특수화된 버전은 True 유형에서, 일반 버전은 False 유형에서 파생된다. 실제 Boost 헤더를 사용하는 예제(목록 12)를 고찰해보자.


목록 12. 코드에서 is_array<T> 및 is_pointer<T> 특성 사용
	
#include <iostream>
#include <boost/type_traits.hpp>

using namespace std;

int main() 
{
    cout << boost::is_array<int[10]>::value << endl;  // outputs 1
    cout << boost::is_array<int[ ]>::value << endl;  // outputs 1
    cout << boost::is_array<int*>::value << endl;  // outputs 0
    cout << boost::is_pointer<int[ ]>::value << endl;  // outputs 0
    cout << boost::is_pointer<float*>::value << endl; // outputs 1
}

그것은 is_pointer<T>였다. 목록 13is_pointer<T>가 어떻게 구현될 것인지를 보여준다. (포인터를 위한 부분 특수화가 예상되는 경로이다.)


목록 13. 일반적인 is_pointer<T> 구현
	
template <typename T> 
struct is_pointer : public false_type{};

template <typename T> 
struct is_pointer<T*> : public true_type{};

is_enum과 같은 일부 유틸리티의 경우 이런 구현을 쉽게 수행할 방법이 없고, 구현을 통해 바람직한 결과를 달성하려면 특정 컴파일러 소스에 의존해야 한다. 이에 따라 코드가 플랫폼에 따라 달라지게 되므로, 세부사항은 Boost 문서를 참조한다.


클래스를 복사할 수 없게 하는 것이 Boost의 방식

클래스를 복사할 수 없게 해야 하는 경우 일반적인 방법은 복사 생성자와 클래스의 할당 연산자를 개인용으로 만들거나 보호되도록 하는 것이다. 이 둘 중 아무 것도 정의되지 않은 경우 컴파일러는 공용 멤버 함수인 암시적 버전을 제공한다. Boost는 noncopyable.hpp 헤더에서 정의된 noncopyable 클래스를 제공하여 이 작업을 더욱 쉽게 완료할 수 있는 방법을 제시한다. 개발자 고유의 클래스를 복사할 수 없게 하려면 단순히 이 클래스에서 파생하면 된다. 파생의 공용, 보호 또는 개인용 여부는 중요하지 않고, 해당 클래스는 항상 noncopyable이다. 목록 14에서는 파생 완료 방법을 보여준다.


목록 14. Noncopyable 클래스에서 파생
	
#include <boost/noncopyable.hpp>
#include <iostream>

class A : public boost::noncopyable { 
public: 
   A( ) { std::cout << “In A\n” << std::endl; }
};

이제, 목록 15에서 선언된 A 클래스에 대한 복사 생성 및 연산자 할당을 사용해보자.


목록 15. 복사할 수 없는 오브젝트에 대한 복사 생성자 및 연산자 할당 사용
	
int main()
{
    A object1;
    A object2(object1);
    object1 = object2;
    return 0;
} 

목록 16에 오류 로그가 표시되어 있다.


목록 16. 목록 14에서 코드를 컴파일할 때의 오류 로그
	
/usr/include/boost/noncopyable.hpp: In copy constructor 
	‘<unnamed>::DontTreadOnMe::DontTreadOnMe
	(const<unnamed>::DontTreadOnMe&)’:
/usr/include/boost/noncopyable.hpp:27: error: 
	‘boost::noncopyable_::noncopyable::noncopyable
	(const boost::noncopyable_::noncopyable&)’ 
	is private

/usr/include/boost/noncopyable.hpp: In member function 
	‘<unnamed>::DontTreadOnMe&<unnamed>::
	DontTreadOnMe::operator=(const<unnamed>::DontTreadOnMe&)’:
/usr/include/boost/noncopyable.hpp:28: error: 
	‘const boost::noncopyable_::noncopyable& 
	boost::noncopyable_::noncopyable::operator=
	(const boost::noncopyable_::noncopyable&)’ 
	is private

copy constructoroperator=가 private으로 선언되어 있으므로, noncopyable 클래스 정의에 놀랄 일은 없다. 목록 17에서는 클래스 선언을 보여준다.


목록 17. Noncopyable 클래스 선언
	
class noncopyable
  {
   protected:
      noncopyable() {}
      ~noncopyable() {}
   private:  // emphasize the following members are private
      noncopyable( const noncopyable& );
      const noncopyable& operator=( const noncopyable& );
  };

목록 17에서 유념해야 할 유일한 다른 점은 copy constructoroperator= 메소드에 대한 정의가 제공되지 않는다는 점이다. 이런 정의가 구현되었더라면 고유의 private 메소드 내에서 noncopyable 클래스를 복사하는 것이 기술적으로 가능했을 것이다. 이 구현을 통해 간결한 컴파일 시간 오류 메시지를 받는다.


어설션 실패 시의 함수 콜백

방어적 프로그래밍의 목적은 코드에서 적당한 곳에 어설션을 포함시키는 것이다. 그러나 어설션이 실패하는 경우 어떻게 될까? 일반적으로, 어설션이 실패한 곳(파일 이름 또는 행 번호)을 알게 되고 해당 코드를 인쇄한 메시지를 선택적으로 표시하도록 할 수도 있을 것이다. Boost에서는 훌륭한 콜백 메커니즘이 제공된다. 표현식에서 False로 평가하고 그에 따라 어설션 실패를 트리거하는 경우 헤더 파일 assert.hpp에서 선언된 assertion_failed라는 미리 정의된 루틴이 실행된다. 목록 18assertion_failed를 사용하는 샘플 코드이다.


목록 18. 어설션 실패 시 assertion_failed를 사용한 프로그램 작동 정의
	
#include <iostream>
using namespace std;
 
#define BOOST_ENABLE_ASSERT_HANDLER
#include <boost/assert.hpp>
 
namespace boost { 
void assertion_failed(char const * expr, char const * function, 
                             char const * file, long line)
{
   cout << expr << endl;  // the offending expression 
   cout << function << endl;  // calling function 
   cout << file << endl;   // file which contains the assertion
   cout << line << endl;  // line number where assert failed
}
}
 
int main( )
{ 
   BOOST_ASSERT(2 > 3);
} 

assertion_failed 함수가 헤더 assert.hpp에 선언되어 있지만 정의되지는 않았다. 이 함수에 대한 정의를 제공해야 한다. 또한, 매크로 BOOST_ENABLE_ASSERT_HANDLER를 정의한 후 애플리케이션 코드에 assert.hpp 헤더를 포함시켜야 한다. 목록 18의 출력에 모든 것이 설명되어 있다. BOOST_ASSERT가 실패하는 순간 assertion_failed가 호출된다.

2 > 3
int main()
prog.cpp
20

BOOST_ENABLE_ASSERT_HANDLER가 정의되어 있지 않은 경우에는 BOOST_ASSERT의 작동이 일반적인 assert의 작동과 똑같다.


두 변수를 스왑하기 위한 Boost 유틸리티

스와핑 변수는 모든 프로그래머가 귀찮지만 매일같이 다루어야 하는 것이다. boost/swap.hpp 헤더에서 사용 가능한 템플리트 함수 template<class T> void swap (T& left, T& right)을 통해 두 변수의 값을 스왑할 수 있다. 그렇다면 STL에서 이미 std::swap을 제공하는데도 boost::swap이 흥미를 끄는 이유는 무엇일까? std::swap의 작동은 다음과 같은 의미를 가진다.

template <class T> void swap ( T& a, T& b )
{
  T c(a); 
  a=b; 
  b=c;
}

대량의 데이터를 저장하는 클래스의 경우, swap에는 하나의 copy construction과 두 개의 할당이 관련되기 때문에 이 방법이 가장 효율적으로 데이터를 스왑하는 방법이 아닐 수도 있다. 또한, 개인용 생성자가 있거나 복사 생성자가 없다는 설계상의 이유가 있는 클래스의 경우, 이런 스타일의 스와핑은 작동하지 않을 것이다. boost::swap의 특징은 다음과 같다.

  • T 유형의 배열을 스왑할 수 있지만, std::swap은 그럴 수 없다.
  • 기본적으로 하나의 copy constructor와 두 개의 할당 옵션 대신 swap(T&, T&) 시그니처가 있는 경우 boost::swap은 이 시그니처를 포함한 함수를 호출할 수 있다.
  • boost::swapstd::swap의 템플리트 특수화를 호출할 수 있다.
  • 위에서 두 번째 옵션과 세 번째 옵션 모두 유효한 옵션이 아닐 경우 T는 복사 생성 및 할당 가능해야 한다.

목록 19에서는 두 배열을 스왑하기 위해 사용되는 boost::swap을 보여준다.


목록 19. boost::swap을 사용하여 두 배열 스왑
	
#include <boost/swap.hpp>
#include <boost/foreach.hpp>
#include <iostream>
using namespace std;
 
int main()
{
  int a[] = {10, 20, 30, 40};
  int b[] = {4, 3, 2, 1};
 
  boost::swap(a, b); // using std::swap here won't work 
 
  BOOST_FOREACH(int t, a) { cout << t << endl; }
  BOOST_FOREACH(int t, a) { cout << t << endl; }
}

목록 20은 사용자 정의 스왑 루틴을 호출하는 boost::swap의 예제이다.


목록 20. boost::swap을 사용한 사용자 정의 스왑 구현
	
#include <boost/swap.hpp>
#include <iostream>
using namespace std;
 
typedef struct T { 
  int m_data;
  T(int data) : m_data(data) { }
} T;
 
void swap(T& a, T& b) // custom swap routine that boost ::swap calls
{
  cout << "In custom swap" << endl;
  a.m_data ^= b.m_data;
  b.m_data ^= a.m_data;
  a.m_data ^= b.m_data;
}
 
int main()
{
  T a(30), b(10);
  boost::swap(a, b);
  cout << a.m_data << endl;
  cout << b.m_data << endl;
}

마지막으로, 목록 21에 템플리트 특수화 버전이 표시되어 있다.


목록 21. std::swap의 템플리트 특수화 버전 사용
	
#include <boost/swap.hpp>
#include <iostream>
using namespace std;
 
typedef struct T { 
  int m_data;
  T(int data) : m_data(data) { }
} T;
 
namespace std { 
template<
void swap<T> (T& a, T& b) 
{
  cout << "In template-specialized swap" << endl;
  a.m_data ^= b.m_data;
  b.m_data ^= a.m_data;
  a.m_data ^= b.m_data;
}
}
 
int main()
{
  T a(30), b(10);
  boost::swap(a, b);
  cout << a.m_data << endl;
  cout << b.m_data << endl;
}

이제, boost::swap을 내부적으로 어떻게 구현하는지 살펴보자. swap...for 배열의 정의 방법이 특별한 관심사이다. 목록 22는 boost/swap.hpp에서 복사되는 코드를 나타낸 것이다.


목록 22. boost::swap을 위한 소스 코드
	
#include <algorithm> //for std::swap
#include <cstddef> //for std::size_t

namespace boost_swap_impl
{
  template<class T>
  void swap_impl(T& left, T& right)
  {
    using namespace std;//use std::swap if argument dependent lookup fails
    swap(left,right);
  }

template<class T, std::size_t N>
  void swap_impl(T (& left)[N], T (& right)[N])
  {
    for (std::size_t i = 0; i < N; ++i)
    {
      ::boost_swap_impl::swap_impl(left[i], right[i]);
    }
  }
} 

namespace boost
{
  template<class T1, class T2>
  void swap(T1& left, T2& right)
  {
    ::boost_swap_impl::swap_impl(left, right);
  }
}

배열의 경우, boost::swap을 호출하면 배열에 대해 동일한 코드가 특수화되므로 결국 무효 swap_impl(T (& left)[N], T (& right)[N])에 대한 호출이 된다. swap_impl(T (& left)[N], T (& right)[N]) 선언을 살펴보자. 여기서, leftright는 유형 T와 크기 N의 배열에 대한 참조이다. 두 배열 모두 같은 크기여야 하며, 그렇지 않으면 컴파일 오류가 발생한다. 다른 모든 경우에는 swap_impl(T& left, T& right)이 호출된다. swap_impl(T& left, T& right)의 정의를 살펴보면, swap 루틴을 호출한다는 사실을 알 수 있다. std::swap의 템플리트 특수화 버전을 가지고 있거나(목록 21을 다시 참조할 것) 글로벌 swap 루틴을 가지고 있는 경우(목록 20을 다시 참조할 것) 이와 동일한 것이 호출된다. 그렇지 않으면 std::swap이 호출된다.

마지막으로, T1을 사용하는 것으로 충분했겠지만, swap의 선언에 template<typename T1, typename T2>가 있다는 점에 주목하자. 이 선언으로 std::swap보다 덜 특수화되므로, 이는 의도적인 것이다. 같은 범위에서 boost::swapstd::swap을 사용할 수 있는 경우 swap에 대한 호출이 std::swap에 우선권을 줄 것이다.


결론

이것으로 본 기사를 마무리하겠다. 우리는 사용법의 관점에서 압축된 쌍, 유형 특성, non-copyable 클래스, 사용자 정의 어설션 처리 및 사용자 정의 스와핑과 같이 꽤 많은 수의 흥미로운 유틸리티를 살펴보았으며, 가급적이면 그 본질도 이해하려고 해보았다. 엄밀히 말하자면, 이런 유틸리티를 사용하기 위해 Boost 헤더의 본질을 꼭 이해할 필요가 있는 것은 아니지만, 이 부분까지 이해하면 Boost 헤더의 다각적인 측면을 더욱 쉽게 활용할 수 있다. 특히, Boost가 제공하는 여러 가지 트릭은 성능 면에서 효율적인 코드와 라이브러리를 개발하는 데 도움이 된다.


참고자료

교육

  • David Vandevoorde와 Nicolai M. Josuttis가 공동 집필한 C++ Templates: The Complete Guide(Addison-Wesley Professional, 2002년)를 읽어보자.

  • AIX와 UNIX developerWorks 영역: AIX와 UNIX 영역에서는 AIX 시스템 관리와 UNIX 스킬 확장의 모든 측면과 관련된 풍부한 정보를 제공한다.

  • AIX와 UNIX 입문 AIX와 UNIX 입문 페이지에서 자세한 정보를 볼 수 있다.

  • 기술 서점: 다양한 기술 주제와 관련된 서적을 살펴볼 수 있다.

제품 및 기술

  • 무료로 IBM 소프트웨어를 체험해보자. 평가판을 다운로드하고 온라인 평가판에 로그인하며 샌드박스 환경에서 제품으로 작업하거나 클라우드를 통해 이에 액세스하자. 100개 이상의 IBM 제품 체험판에서 선택하자.

  • Boost C++ libraries에 대해 자세히 알아보자.

토론

필자소개

Arpan Sen은 선임 엔지니어로 전자 설계 자동화 분야의 소프트웨어 개발을 담당한다. 여러 해 동안 Solaris, SunOS, HP-UX 및 IRIX를 포함한 UNIX는 물론 Linux와 Microsoft Windows의 여러 제품과 관련된 업무를 맡아 왔다. 소프트웨어 성능 최적화 기법, 그래프 이론 및 병렬 컴퓨팅에 많은 관심을 가지고 있다. Arpan은 대학원에서 소프트웨어 시스템 분야를 전공했다. 이메일 주소는 arpansen@gmail.com이다.

잘못된 도움말 신고

부정사용 신고

감사합니다. 이 항목은 운영자가 관심을 표시했습니다.


잘못된 도움말 신고

부정사용 신고

제출실패 신고. 나중에 다시 실행해주세요.


디벨로퍼웍스 로그인


IBM ID가 필요하세요?
IBM ID를 잊으셨습니까?


비밀번호를 잊으셨습니까?
비밀번호 변경

developerWorks 이용 약관에 동의하시는 경우 제출을 클릭하십시오. 이용 약관.

 


developerWorks에 처음 로그인하면 developerWorks프로파일이 생성됩니다.귀하의 프로파일에서 동의하신 내용이 공개되지만 이 사항은 언제든지 변경 가능합니다. 귀하의 성명(숨김으로 체크되어 있어도 표시됩니다)과 디스플레이 이름은 게시한 컨텐츠나 사이트 엑세스시 표시됩니다.

화면상에 보여지는 닉네임을 정하세요.

처음 developerWorks에 로그인할 때 프로파일이 작성되므로, 이를 위해 디스플레이 이름을 선택해야 합니다. 선택하신 디스플레이 이름은 developerWorks에 게시한 컨텐츠에 표시됩니다.

3글자 이상 31글자 이하의 길이로 사용 가능합니다. dW커뮤니티 내에서는 보안상 이메일주소를 제외한 다른 이름을 지정하셔야 합니다.

3개의 &이나 대쉬를 포함해주시고 31글자내로 제한해주세요.


developerWorks 이용 약관에 동의하시는 경우 제출을 클릭하십시오. 이용 약관.

 


아티클 순위

의견

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=20
Zone=AIX와 UNIX
ArticleID=783224
ArticleTitle=추가 Boost 유틸리티
publish-date=12282011

태그

Help
검색 필드를 사용하여 My developerWorks 내에서 해당 태그가 사용된 모든 종류의 컨텐츠를 검색하십시오.

태그를 더 많이 보거나 적게 보기 위해 슬라이더 막대를 사용하십시오.

인기 태그는 특정 컨텐츠 존(예를 들어, 자바, 리눅스, WebSphere)의 최고 인기 태그를 보여줍니다.

내 태그는 특정 컨텐츠 존(예를 들어, 자바, 리눅스, WebSphere)의 귀하의 태그를 보여줍니다.

검색 필드를 사용하여 My developerWorks 내에서 해당 태그가 사용된 모든 종류의 컨텐츠를 검색하십시오. 인기 태그는 특정 컨텐츠 존(예를 들어, 자바, 리눅스, WebSphere)의 최고 인기 태그를 보여줍니다. 내 태그는 특정 컨텐츠 존(예를 들어, 자바, 리눅스, WebSphere)의 귀하의 태그를 보여줍니다.