이펙티브 C++
https://www.yes24.com/Product/Goods/17525589
페이지 381p ~ 392p
이펙티브 C++(381p ~ 392p)
요약
항목 54: TR1을 포함한 표준 라이브러리 구성요소와 편안한 친구가 되자
- C++은 언어 자체의 기능 외의 새로운 기능들 대부분은 기존의 표준 라이브버리에 추가되는 형태로 버전업이 되고 있다.
TR1
(1차 기술 보고서, Technical Report 1)이 이에 포함된다.TR1
이 포함된 것을 가리켜 C++ 표준 1.1이라고 부르는 경우를 볼 수 있는 것도 이런 의미 때문이며,TR1
의 기능은 사실상 모든 종류의 라이브러리와 응용프로그램에 영향을 미치고 있다.TR1
에 무엇이 들어 있는지 살펴보는 역사적인 순간에, C++98에 명시되어 있는 표준 C++ 라이브러리의 주요 구성요소를 되짚어 보는 것도 좋을 것이다.
표준 템플릿 라이브러리(Standard Template Library: STL)
- 주요 구성요소로서 컨테이너(vector, string, map 등), 반복자, 알고리즘(find, sort, transform 등), 함수 객체(less, greater) 외에 이런저런 컨테이너 어댑터와 함수 객체 어댑터(stack, priority_queue, mem_fun, not1 등)가 있다.
iostream
- 사용자 정의 버퍼링, 국제화 기능이 가능한 입출력을 지원하며, 그 외에 cin, cout, cerr, clog 등의 사전정의 객체를 지원한다.
국제화 지원
- 여러 로케일(locale)을 활성화시킬 수 있는 기능이 포함되어 있다. 또한
wchar_t
등의 타입(대개 16비트/문자) 및wstring
(wchar_t
타입으로 정의한 string)을 쓰면 유니코드를 사용할 수 있다.
수치 처리 지원
- 복소수를 나타내는 템플릿(complex) 및 수치 배열을 나타내는 템플릿(valarray)이 여기에 해당된다.
예외 클래스 계통
- 최상위 클래스인
exception
및 이것으로부터 갈라져 나온 파생 클래스들, 예를 들어logic_error
및runtime_error
등이 여기에 포함된다.
C89의 표준 라이브러리
1989년 버전의 C에 포함된 표준 라이브러리는 전부 C++에도 들어 있다.
TR1
을 통해 명시된 새로운 구성요소는 총 14개이다.14개 모두
std
네임스페이스에 들어 있는데, 더 정확히 말하면std
안에 중첩된tr1
이란 네임스페이스에 있다.이 책에서 예제 코드에 등장한
TR1
의 구성요소부터 정리하면 다음과 같다.
스마트 포인터
tr1::shared_ptr
,tr1::weak_ptr
이 여기에 해당한다. 동작은 기본제공 포인터와 똑같으나, 하나의 실체 객체를 가리키는 자신과 같은 포인터의 개수를 유지해 놓는 똑똑한 포인터이다.- 이런 기법을 가리켜 참조 카운팅이라고 부른다.
- 어떤 객체를 가리키는 최후의 스마트 포인터가 소멸될 때(참조 카운트가 0이 될 때), 그 객체도 자동으로 삭제한다.
- 스마트 포인터는 비순환형 자료구조 내의 자원 누출을 방지하는 용도에 아주 적합하다.
- 만약, 두 개 이상의 객체에
tr1::shared_ptr
이 하나씩 들어 있고 각각의 스마트 포인터가 이들 객체를 물고 있는 형태가 순환 구조를 이루고 있으면, 각 객체의 참조 카운트가 0이 안 될 수가 있다. - 이런 상황을 막기 위해 쓰는 것이
tr1::weak_ptr
이다.tr1::shared_ptr
에 기반한 비순환형 자료구조에서 순환고리를 가능하게 하는 포인터로 동작하게끔 설계되었다. tr1::weak_ptr
은 참조 카운팅에는 가담하지 않는다. 어떤 객체를 물고 있는 마지막tr1::shared_ptr
이 소멸될 때,tr1::weak_ptr
이 여전히 계속해서 그 객체를 가리키고 있더라도 그 객체는 삭제된다. 단, 이 경우의tr1::weak_ptr
은 무효상태(invalid)로 표시된다.
tr1::function
- 어떤 함수가 가진 시그니처와 호환되는 시그니처를 갖는 함수호출성 객체(callable entity)의 표현을 가능하게 해 주는 템플릿이다.
- 시그니처가 비슷하면 호출이 가능한 일반화 콜백 함수를 만들어 보자는 것이 주요 개념이다.
- 보통, int를 받고 string을 반환하는 콜백 함수를 등록하도록 만들고 싶으면 다음과 같이 한다.
// int를 받고 string을 반환하는 함수가 매개변수 타입
void registerCallback(std::string func(int));
- 매개변수 이름으로 쓰인
func
는 사실 없어도 되는 선택사항이므로, 다음과 같이 이름을 떼고 선언해도 문제가 없다.
void registerCallback(std::string (int));
- 여기서 매개변수 타입에 해당하는
std::string(int)
부분이 바로 함수 시그니처이다. - 이런 용도에
tr1::function
템플릿을 사용하면registerCallback
함수가 훨씬 융통성 있게 만들어진다. - 함수 시그니처 대신에 임의의 함수호출성 개체를 받도록 만들게 함으로써,
int
타입 혹은int
로 변환이 가능한 어떤 타입도 전달받으며,string
타입 혹은string
으로 변환이 가능한 어떤 타입도 반환할 수 있는 그런 함수를registerCallback
의 매개변수로 설정할 수 있게 된다. tr1::function
의 템플릿 매개변수로는 앞의 그 시그니처를 넘긴다.
// 매개변수 func는 이제 std::string(int)와 호환되는 시그니처를 갖는
// 어떤 함수호출성 개체도 될 수 있다.
void registerCallback(std::tr1::function<std::string(int)> func);
tr1::bind
현역 STL 바인더로 잘 쓰이고 있는 bind1st 및 bind2nd와 똑같이 동작함은 물론, 그보다 훨씬 더 많은 기능이 있는 범용 바인더이다.
TR1
이전의 바인더들과 달리,tr1::bind
함수는 상수 멤버 함수 및 비상수 멤버 함수에 상관없이 동작한다. 또한 참조로 전달되는 매개변수에 대해서도 동작한다.외부 보조 없이도 함수 포인터를 자체적으로 다룰 수 있기 때문에,
tr1::bind
를 호출하기 전에ptr_fun
,mem_fun
혹은mem_fun_ref
를 우겨 넣는 난리 부르스를 출 필요가 없다.TR1
의 나머지 구성요소는 두 종류로 나누어 소개한다. 우선, 단독으로 딱딱 끊어지는 기능을 제공하는 종류부터 알아보자.
해시 테이블(hash table)
- 새로운 동작 원리로 무장한 연관 컨테이너 군단, 즉 set, multiset, map, multimap을 구현하는 데 이 해시 테이블이 쓰였다.
- 이들 각각의 인터페이스는 똑같은 이름을 가진
TR1
이전의 연관 컨테이너의 인터페이스를 본떠서 만들어졌다. TR1
해시 테이블 기반 컨테이너는unordered_map
,unordered_multimap
,unordered_set
,unordered_multiset
이라는 이름을 가지고 있는데, 이는 해시 기반 컨테이너이기 때문에 원소가 저장되는 순서를 예측할 수 없다는 점을 강조하고 있다.- 기존은
map
,multimap
,set
,multiset
이라는 이름을 가지고 있다.
- 기존은
정규 표현식(regex expression)
- 정규 표현식 기반의 탐색과 문자열에 대한 대체 연산이 가능하며, 일치되는 원소들 사이의 순회도 지원한다.
투플(tuple)
- 종래의 표준 라이브러리에 원래 있었던
pair
템플릿의 신세대 버전이다. pair
객체의 경우에는 두 개만 담을 수 있었지만,tr1::tuple
객체는 몇 개라도 담을 수 있다.
tr1::array
begin
및end
등의 멤버 함수를 지원하는 배열이다.tr1::array
객체의 크기는 컴파일 과정에서 고정된다.(동적 메모리를 쓰지 않는다.)
tr1::mem_fn
- 멤버 함수 포인터를 적응시키는(adapt) 용도에 쓸 수 있는, 문법적으로 C++98의
mem_fun
및mem_fun_ref
를 그대로 껴안음과 동시에 기능을 확장한 템플릿이다.
tr1::reference_wrapper
- 기존의 참조자가 객체처럼 행세할 수 있도록 만들어 주는 템플릿이다.
- 이것을 사용하면 참조자를 담은 것처럼 동작하는 컨테이너를 만들 수 있다.(사실 컨테이너는 객체 혹은 포인터만 담을 수 있다.)
난수 발생
- C++가 C 표준 라이브러리로부터 물려받은
rand
함수보다 몇 배는 우수한 난수 발생 기능이다.
특수 용도의 수학 함수
- 라게르 다항식, 베셀 함수, 완전 타원 적분 등이며 그 외에도 많이 있다.
C99 호환성 확장 기능
C99의 새로운 라이브러리를 C++로 가져올 목적으로 설계된 함수 및 템플릿 모음
나머지
TR1
구성요소의 두 번째 부분은 더 세련된 템플릿 프로그래밍 기법, 이를테면 템플릿 메타프로그래밍 같은 기법을 지원하는 기술들이다.
타입 특성정보(type traits)
- 주어진 타입에 대한 컴파일 타임 정보를 제공하는 특성정보 클래스의 모음이다.
- 어떤 T라는 타입에 대해
TR1
의 타입 특성정보 기능을 적용하면, T가 기본제공 타입인지, 가상 소멸자를 지원하는지, 공백 클래스인지, 다른 U 타입으로 암시적 변환이 가능한지 등의 정보를 들추어낼 수 있다. - 심지어 주어진 타입의 적절한 바이트 정렬까지 잡아내어 준다.
- 사용자 정의 메모리 할당 함수를 제작하는 프로그래머에게는 희소식이다.
tr1::result_of
어떤 함수 호출의 반환 타입을 추론해주는 템플릿이다.
템플릿을 만들다 보면 어떤 함수(템플릿)의 호출로 인해 반환되는 객체의 타입을 참조할 수 있으면 좋겠다는 생각이 꽤 자주 들지만, 반환 타입은 그 함수의 매개변수 타입에 따라 달라질 수 있다.
tr1::result_of
템플릿을 쓰면 함수 반환 타입을 쉽게 참조할 수 있다.이 템플릿은
TR1
자체적으로도 여러 군데에서 사용하고 있다.TR1
구성요소들 중에는TR1
이전의 구성요소들 몇 개에 대해서 기능을 그대로 끌어안은 것들이 있긴 하지만(tr1::bind
와tr1::mem_fn
이 대표적이다.), 현재의TR1
은 그저 표준 라이브러리에 대한 부가 구성요소일 뿐이다.기존의 구성 요소 대신에
TR1
이 쓰일 수 있다는 이야기가 절대로 아니므로,TR1
이전의 것을 써서 만든 재래식 코드는 그대로 유효하다.또한,
TR1
자체는 그냥 문서이다. 이 문서에 명시된 기능을 맛보려면, 문서의 내용을 실제로 구현한 코드를 구해야 한다.결국
TR1
코드는 컴파일러와 짝을 이루어 제공될 것이다.사용하고 있는 표준 라이브러리 구현에서
TR1
구성요소를 찾으려고 하면 몇 개가 빠져 있을 수도 있다.- 이런 경우에는
TR1
의 구성요소 중 10개는 부스트에서 무료로 공개한 라이브러리를 기반으로 한 것이라서,TR1
스러운 기능을 맛보는 데는 부스트가 가장 좋은 자원이라 할 수 있다. - 물론 부스트의 것과
TR1
의 명세가 완전히 맞지 않는 부분도 존재하기 때문이다.
- 이런 경우에는
컴파일러에서
TR1
구현을 자체적으로 지원하지 않고 있어 임시변통으로 부스트의TR1
스러운 라이브러리를 써볼려면 꼼수를 부려야 한다.부스트 라이브러리에 속한 모든 구성요소는
boost
네임스페이스에 들어 있지만,TR1
구성요소는std::tr1
에 들어 있어야 하는 규칙이 있다.따라서 컴파일러에게
std::tr1
에 대한 참조를boost
에 대한 참조로 처리해 달라고 부탁해야 한다.
namespace std {
// std::tr1 네임스페이스는 이제 boost 네임스페이스의 별칭으로 설정된다.
namespace tr1 = ::boost;
}
기술적으로 볼 때, 이러한 꼼수를 부렸을 때의 결과는 정의되어 있지 않다.
그 이유는
std
네임스페이스에는 일개 사용자 수준에서 어떤 것도 추가할 권한이 없기 때문이다.이론적으로는 그렇지만, 실제로는 아무런 사고도 안 날 가능성도 있다.
컴파일러에서 자체적으로
TR1
을 제공한다면 위의 별칭을 없애면 된다.부스트 라이브러리에 뿌리를 두고 있지 않은
TR1
의 구성요소 중 가장 중요한 부분을 꼽는다면 아마 해시 테이블일 것이다.해시 기반 컨테이너는 과거에도 몇 군데에서 내놓은 제품을 통해 어렵지 않게 접할 수 있을 것이다.
항목 55: Boo자유친! 부스트를 늘 여러분 가까이에
부스트는 C++ 개발자들의 단체이자 무료 다운로드가 가능한 C++ 라이브러리 집합을 동시에 일컫는 고유명사이다.
http://boost.org
에서 관련 정보를 만날 수 있다.부스트는 다른 곳에서는 따라올 수 없는 차별점이 두 개나 있다.
첫째, 부스트는 C++ 표준화 위원회와 밀접하고 영향력 있는 밀월관계를 유지하고 있는 곳으로 유일하다.
- 원래부터 위원회에 속해 있던 회원들 몇 명이 세운 곳이다.
- 위와 같은 이유로 이름만 들어도 알 법한 유명한 분들이 부스트 및 현재의 표준화 위원회에 양다리를 걸치고 있는 경우가 정말 많다.
- 부스트가 세워진 이래로 꾸준히 지켜왔던 활동목표들 중 하나가 표준 C++에 추가될 수 있는 기능들의 시험 장소로 바치는 것이었고, 그 노력이 결실을 맺어,
TR1
에서 추가된 14개의 라이브러리 중 무려 3분의 2 이상이 이미 부스트에서 배포하고 있는 것들이다.
둘째, 라이브러리 승인 과정이다.
- 부스트의 승인 과정은 공개 동료 심사에 기반을 두고 있다.
- 자신이 만든 라이브러리를 부스트에 한 번 내 보고 싶다면, 일단 부스트 개발자 메일링 리스트에 글을 올려서 라이브러리가 얼마나 관심을 끄는지 알아본다.
- 어느 정도 됐다 싶으면 사전 검사가 시작되는데, 부스트 사이트에서 말하는
토론-수정-다시 제출
이 과정을 만족할 때까지 반복의 고리를 돌게 된다. - 이 과정이 끝나면 라이브러리를 공식적으로 제출하기 위한 형식을 갖추게 되는데, 이때 심사 관리자가 라이브러리가 부스트의 최소 요구사항을 만족하는지를 확인해 준다.
- 부스트 라이브러리가 되려면 일단 컴파일이 되는 컴파일러가 최소한 두 개는 되어야 한다.
- 일정한 라이센스에 따라서 배포할 수 있는지의 여부 입증
- 이 과정이 끝나면 비로소 여러분의 것이 부스트의 쟁쟁한 개발자 모임에 공식 심사 대상으로 제출된다.
- 심사는 제출물(소스 코드, 설계 문서, 사용자 문서 등)을 검토하면서 다음과 같은 사항에 중점을 두어 진행된다.
- 설계와 구현이 얼마나 우수하게 되어 있나?
- 다른 컴파일러와 운영체제들에 맞추어 이식할 수 있는 코드인가?
- 대상 사용자들, 다시 말해 이 라이브러리가 도움을 줄 수 있는 특정 분야 종사자에게 쓸모 있는 라이브러리인가?
- 문서화가 명료하고, 완전하고, 정확하게 되어 있는가?
- 각각에 대한 의견을 담은 심사 결과는 부스트 메일링 리스트를 통해 공개적으로 올라온다.
- 그렇기 때문에 심사를 진행하는 당사자와 그 외의 사람들이 그 심사 결과를 보고 다른 의견을 나눌 수도 있게 된다.
- 심사 기간이 끝날 무렵이 되면, 심사 관리자는 그간의 의견을 취합하여 여러분의 라이브러리에 대해 승인, 조건부 승인, 기각을 결정한다.
이러한 동료 심사 시스템 덕택에 함량 미달의 라이브러리가 부스트에 발을 들여 놓는 일이 효과적으로 차단되었을 뿐만 아니라, 라이브러리를 한 번 제대로 만들어 보고 싶은 사람들에게 업계 수준의 교차 플랫폼 라이브러리를 설계, 구현하고 문서화하는 방법이 자연스럽게 전수되는 효과를 낳았다.
부스트의 이름하에 배포되는 라이브러리로서 승인이 되려면 최소한 두 번 이상의 공개 심사는 기본이라는 것이 이 동네의 상식이다.
부스트의 라이브러리 군단은 크게 십수 개의 범주로 나뉘어 있는데, 각각을 소개하면 다음과 같다.
문자열 및 텍스트 처리
- 주요 구성요소로 타입 안전성을 갖춘
printf
비슷한 서식화 기능, 정규 표현식 및 토큰화와 구문분석 기능이 있다.
컨테이너
- STL 양식의 인터페이스를 제공하는 고정 크기 배열, 가변 크기 비트세트, 다차원 배열 등이 포함되어 있다.
함수 객체 및 고차 프로그래밍
TR1
의 기능을 구현하는 데 사용된 몇 개의 기반 라이브러리가 여기에 해당된다.- 람다 라이브러리는 별도의 준비 없이 즉석에서 함수 객체를 생성해 주는 기막힌 기능을 제공하는데, 여러분 자신도 무엇을 했는지를 자각하지 못할 정도로 감쪽같다.
using namespace boost::lambda; // boost의 lambda 기능을 쓸 수 있도록 만듬
std::vector<int> v;
// v안에 들어 있는 각각의 원소 x에 대해 x*2+10을 출력한다.
// _1은 람다 라이브러리에서 사용하는 '현재 원소'를 뜻하는 자리채움자이다.
std::for_each(v.begin(), v.end()),
std::cout << _1 * 2 + 10 << "\\n");
일반화 프로그래밍
- 텍사스 소떼처럼 득실득실한 특성정보(traits) 클래스를 만날 수 있다.
템플릿 메타프로그래밍(TMP)
- 컴파일 타임 단정문, 부스트 MPL 라이브러리 등이 여기에 포함된다.
- MPL에는 아주 감동적인 물건들이 많지만 그 중 하나를 꼽는다면 타입 등의 컴파일 타임 개체를 STL스러운 자료구조로 관리할 수 있도록 지원한다는 것이다.
// 세 개의 타입(float, double, long double)을 담는 컴파일 타임
// 컨테이너를 list 비슷하게 만들고, 이것을 'floats'라고 부른다.
typedef boost::mpl::list<float, double, long double> floats;
// 기존의 'floats'에 들어 있는 타입 집합은 물론이고 그 집합의 앞에 int가 삽입된
// 새로운 타입 리스트를 만들고, 이것을 'types'라고 부른다.
typedef boost::mpl::push_front<floats, int>::type types;
- 타입을 담아 관리할 수 있는 이런 컨테이너는 타입리스트라고 알려져 있다.
수학 및 수치 조작
- 유리수, 4원수 및 8원수, 최대 공약수 및 최소 공배수, 난수 등이 포함된다.
정확성 유지 및 테스트
- 암시적 템플릿 인터페이스를 형식화해 주는 라이브러리와 테스트 우선 프로그래밍을 가능하게 해 주는 라이브러리가 있다.
자료구조
- 타입 안전성을 갖춘 공용체, 그리고
TR1
에서 지원하는 것의 뿌리격인 바로 그 투플 라이브러리가 이 범주에 들어간다.
타 언어와의 연동 지원
- C++와 파이썬 사이의 걸림돌 없는 상호운용을 가능하게 하는 라이브러리도 지원한다.
메모리
- 고성능의 고정 크기 할당자를 지원하는 풀(Pool) 라이브러리.
TR1
에도 일부 포함된 가지각색의 스마트 포인터가 이 범주에 들어간다. TR1
에 들어가지 않은 스마트 포인터 중 하나가scoped_array
이다. 동적으로 할당된 배열에 대해 동작하는auto_ptr
같은 스마트 포인터라고 보면 된다.
기타
CRC 점검, 날짜 및 시간 조작, 파일 시스템 횡단 등을 지원하는 라이브러리가 주요 구성요소이다.
이렇듯 부스트에서 배포하는 라이브러리는 정말 많은 일을 도와주고 있다.
메모
항목 54: TR1을 포함한 표준 라이브러리 구성요소와 편안한 친구가 되자
- 최초에 상정된 표준 C++ 라이브러리의 주요 구성요소는 STL, iostream, 로케일 등이다. 여기에는 C98의 표준 라이브러리도 포함되어 있다.
- TR1이 도입되면서 추가된 것은 스마트 포인터(
tr1::shared_ptr
등), 일반화 함수 포인터(tr1::function
), 해시 기반 컨테이너, 정규 표현식 그리고 그 외의 10개 구성요소이다. TR1
자체는 단순히 명세서일 뿐이다.TR1
의 기능을 사용하기 위해서는 명세를 구현한 코드를 구해야 한다.TR1
구현을 구할 수 있는 자료처 중 한 군데가 바로 부스트이다.
항목 55: Boo자유친! 부스트를 늘 여러분 가까이에
- 부스트는 동료 심사를 거쳐 등록되고 무료로 배포되는 오픈 소스 C++ 라이브러리를 개발하는 모임이자 웹사이트이다. 또한 C++ 표준화에 있어서 영향력 있는 역할을 맡고 있다.
- 부스트에서 배포되는 라이브러리들 중엔
TR1
구성요소에 들어간 것도 있지만, 그 외에 다른 라이브러리들도 아주 많다.
'책 > 이펙티브 C++' 카테고리의 다른 글
이펙티브 C++(370p ~ 381p) (1) | 2024.02.14 |
---|---|
이펙티브 C++(360p ~ 369p) (0) | 2024.02.13 |
이펙티브 C++(350p ~ 361p) (1) | 2024.02.12 |
이펙티브 C++(340p ~ 350p) (1) | 2024.02.10 |
이펙티브 C++(332p ~ 340p) (1) | 2024.02.09 |