Element+

회사 일로 Xerces C++을 사용하는 중입니다만.. boost와 STL에 찌든 저에게 저런 네이밍은 마음에 안듭니다. 사용법도 복잡하구요.. -_-;

대체물을 물색하다가 libxml2가 떠오르더군요. 지금 이 블로그에서 XML/XSLT 처리기로 사용하고 있는 것이 libxml2/libxslt의 python 바인딩이기도 하고 해서, 잠깐 고려를 해보았습니다. C더군요.. OTL..

Xerces C++을 변경해서 새로운 C++ wrapper를 만들어볼까 했지만, 그다지 마음에 들지도 않고.. 오히려 libxml2가 xpath같은 걸 쉽게 쓸 수 있기도 하고 해서 C++ Wrapper를 만들어보자! 라는 생각이 들었습니다. 몇가지 실험을 해본 결과 만족스럽더군요. operator[ ]를 오버로딩해서 xpath expression을 node에서 바로 쓸 수 있게도 해보고, child를 iterator로 사용할 수 있게도 해보고, iterator도 element만 돌기, text만 돌기 같은 것도 만들어 보구요. 🙂

해서 이름을 붙이려고 하는데, libxml++은 이미 있더군요. (역시나 사용하는 스타일은 매우 마음에 안듭니다.)

사실 libxml2에 있는 기능을 모두 지원할 생각도 없고, xpath를 위주로 한 라이브러리가 될 것 같다는 생각에, Element+라는 이름을 지어보았습니다. 헤헤.

쉬운게 좋은거죠. 히힛.

다만, xpath에 의해 평가된 결과물을 더 쉽게 접근할 수 있는 방법은 좀 더 고민해봐야 할 것 같다는..

목표는 이런거죠 뭐.

element_plus::document doc = element_plus::parse(“test.xml”);
element_plus::xpath_result res = doc.eval(“//text()”);
std::for_each(res.begin(), res.end(), std::ptr_fun(print_text_node));

smart tag 처리기가 [ ]를 지원못해서 어쩔수 없이 eval로… ㅡㅜ

요런 스타일로 쓰게 해주는. 헤헤.

이름도 지어놨고, 만드는 일만 남았네요. 아자!

오픈소스 C++ IDE CodeBlocks

CodeBlocks는 오픈소스 크로스플랫폼 C++ IDE입니다.
그토록 갈구하던 녀석이군요. 으흑. ;ㅁ;

특이한 점이랄까.. 하는 것은 여러 C++ Compiler를 지원한다는 점입니다.

  • GCC (MingW / Linux GCC)
  • MSVC++
  • Digital Mars
  • Borland C++ 5.5
  • Open Watcom


지원하는 군요. 어차피 쓰는 것은 MSVC++과 GCC이긴 하겠지만요. 리눅스에서도 작동하고 윈도우에서도 작동합니다. 특히
윈도우 플랫폼에서는 MingW가 기본으로 포함된 버젼도 있군요. 키야앙!!! – MSVC++은 무료로 제공되는 컴파일러가
있지요.. 후후후.

사실 DevC++같은 툴은 모자란 점이 좀 많아서 불만이었는데, 이건 쓰기 편한점이 많은듯 하네요.

아직, auto-completion같은 기능이 좀 모자란 면이 보이긴 하지만, 해결되리라 생각하고…
디버깅기능도 살짝 모자랍니다. local변수 보기같은… (Watch만 되는 것 같더군요)

MSVC++ 프로젝트 임포트도 지원하고. (버그는 좀 있답니다..)

사실 STL/Boost의 auto-completion만 잘 지원되주기만 해도.. 으흐흐.

CodeBlock의 완성도가 높아진다면, 굳이 Visual C++을 이용할 이유가 사라질 것 같기도 합니다. 🙂

boost::shared_ptr에 관해!

boost::shared_ptr은 boost/smart_ptr.hpp를 이용해 사용할 수 있는 라이브러리입니다.

smart_ptr은 그 이름과 같이, smart pointer들을 다루고 있는 라이브러리로, noncopyable(복사 불가능한) auto_ptr인 scoped_ptr과 레퍼런스 카운팅을 이용해 객체 해제를 자동으로 관리해주는 shared_ptr, shared_ptr에 레퍼런스 카운팅을 하지 않고 단순히 참조만 할 수 있게 해주는 weak_ptr, shared_ptr과 유사한 intrusive_ptr이 있으며, 배열 스타일의 포인터 사용을 위한 scoped_array, shared_array 역시 존재합니다.

지금부터 알아볼 클래스는 바로 boost::shared_ptr로 C++ 프로그래머들이 겪는 Memory Leak을 효과적으로 방지하고, 동적으로 할당한 객체들을 컨테이너에서 손쉽게 관리하도록 해주는 매우 유용한 툴입니다.


1. std::auto_ptr의 함정.

뜬금없이 왠 std::auto_ptr을 언급하냐! 라며 울부짖으실 분도 있으시겠지만, 많은 사람들이 저지르는 실수 중 한가지가 auto_ptr과 관련이 있으며, 이에 대한 해결책 중에 한가지가 shared_ptr이기에 짚고 넘어가려고 합니다.

Scott Meyers의 Effective STL 항목8에 잘 나와있듯이, auto_ptr의 컨테이너는 절대로 만들어서는 안됩니다. 오래 살고 싶으시다면요. ^^

먼저 auto_ptr에 대해 잠깐 알아보기로 하지요.

#include <memory>

void test()
{
//먼저 힙에 int를 하나 만듭시다.
int* int_p = new int;
//auto_ptr_int을 만듭니다! 녀석은 int_p를 관리하게 됩니다.
std::auto_ptr<int> auto_ptr_int(int_p);
*auto_ptr_int = 3; //  int_p에 3을 넣습니다.
std::cout << "hehehe in test()" << std::endl;
// 그냥 심심해서 뿌려본겁니다.
return; // 이때 int_p가 파괴됩니다.
}
&#91;/cpp&#93;

위의 예제에서 알 수 있듯이, int_p는 함수가 종료됨과 함께 파괴됩니다. 그 이유는 auto_ptr때문이지요.

auto_ptr은 자신이 파괴될 때, 자신이 소유한 객체를 파괴합니다. 예제에서는 스택에 auto_ptr을 생성했으므로, 스택이 파괴될때 - 즉, 함수가 종료될 때 - auto_ptr이 파괴됩니다.
소유권 이전의 문제도 알아봅시다.

&#91;cpp&#93;
#include <memory>
void test2()
{
std::auto_ptr<int> ap1;
ap1 = new int; // 새로 생성된 int객체에 대한 소유권은 ap1에 있습니다.
{
std::auto_ptr<int> ap2;
ap2 = ap1; // ap1이 갖고 있던 소유권이 ap2에 이전됩니다.
}
// 블럭이 종료되면서 ap2가 파괴됩니다.
// 따라서, int객체도 파괴되지요.
}
// ap1이 파괴되지만, ap1은 어떤 객체도 소유하고 있지 않으므로
// 아무일도 일어나지 않습니다.

auto_ptr은 위에서 알 수 있듯이, 소유권을 공유할 수 있는 방법은 없습니다. 강제적으로 하더라도, 같은 객체를 소유하고 있는 auto_ptr중 어느 하나라도 파괴된다면, 그 즉시 객체가 파괴되어 버리므로, 프로그램은 엉망이 되어버리겠지요.

auto_ptr의 컨테이너를 만들면 안되는 이유도 같은 이유입니다.

#include <memory>
#include <vector>

// C++ 프로그래머의 영원한 친구 typedef!!!!
typedef std::auto_ptr<int> AIP;
void test3()
{
std::vector<aip> aip_vec;
....(aip_vec에 데이터를 마구마구 넣읍시다.)...
AIP ap1 = aip_vec.at(0);
AIP ap2 = aip_vec.at(0);
std::cout << *ap1 << std::endl; // 문제되지 않습니다.
std::cout << *ap2 << std::endl; // segment fault가 발생합니다.
}
&#91;/cpp&#93;

위의 예제에서 ap1은 aip_vec의 첫번째 AIP(std::auto_ptr<int>)가 가진 int 객체에 대한 소유권을 이전받습니다. 즉 aip_vec[0]는 아무 객체도 소유하고 있지 않지요. 따라서 ap2역시 어떤 소유권도 가질 수 없습니다. 이때 ap2를 참조하게 된다면 문제가 발생 하지요.

컨테이너들은 무조건 값에 의해 작동합니다. 참조나 포인터가 아니지요. 따라서, auto_ptr을 컨테이너에 사용하는 것은 매우 위험할 수 있습니다. 알고리즘이나 멤버함수에 따라 객체가 파괴되어버릴 수 있으니까요!!!

<hr/>
<strong>2. 그렇다면 boost::shared_ptr은?</strong>

boost::shared_ptr은 레퍼런스 카운팅을 이용해 작동합니다. 레퍼런스 카운팅이란, 특정 객체에 대해 카운터를 놓고 참조자가 늘어날 때마다 카운트를 올리고, 참조자가 줄어들때마다 카운트를 내리다가, 참조자가 없어지면 그때 객체를 파괴하는 방식입니다.

앞의 test2를 shared_ptr을 이용해 바꾸고 그 동작을 살펴보기로 하지요.

[cpp]
#include <boost/shared_ptr.hpp>
void test4()
{
boost::shared_ptr<int> sp1;
// 카운트가 1이 됩니다. int객체에 대한 shared_ptr은 sp1뿐이니까요.
sp1 = boost::shared_ptr<int>(new int);
{
boost::shared_ptr<int> sp2;
sp2 = sp1; // int객체에 대한 카운트가 2가 됩니다. sp1, sp2 2개니까요.
}
// sp2가 파괴되면서 카운트가 하나 줄어듭니다. 이젠 1이군요.
}
// sp1이 파괴되면서 카운트가 줄어들어 0이 됩니다.
// 이때 int객체가 파괴됩니다. :)

간단하지요? 카운트가 늘어나고 줄어들고, 0이되면 파괴된다는 사실만 기억하시면 됩니다.
그럼 이번엔 좀 더 복잡한 예를 살펴보기로 하지요. vector를 이용한 예제입니다.

#include <boost/shared_ptr.hpp>
typedef boost::shared_ptr<int> SIP
void test5()
{
SIP sp1 = SIP(new int); // 카운트는 1입니다.
std::vector<sip> sip_vec;
sip_vec.push_back(sp1); // 카운트는 2
sip_vec.push_back(sp1); // 카운트는 3
sip_vec.pop_back(); // 카운트는 2
sip_vec.pop_back(); // 카운트는 1
} // 이때 파괴됩니다.

vector는 값에 의한 의미론이므로, push_back 멤버함수는 복사를 통해 작동하게 됩니다. 즉, sip_vec.push_back(sp1)은 sp1의 사본을 sip_vec에 넣는다 라는 의미이지요. sp1은 복사되면서 카운트를 증가시킵니다. -복사연산자, 복사생성자를 통해서요.- 따라서 push_back이 완료되고 나면 카운트는 2가 되지요.

shared_ptr의 강력함중 하나는 컨테이너에서 객체를 삭제할 경우입니다. shared_ptr을 사용하지 않고, 그냥 포인터를 써보기로 하지요.

#include <vector>
class Test; // 있다고 칩시다.
typedef std::vector<test*> TestVectorPTR;
void test6()
{
Test* t = new Test;
TestVectorPTR v;
v.push_back(t);
v.pop_back();
// t가 지워지질 않습니다아..
// 허허. 당연하죠. vector는 값의미론이니까요.
delete t; // 항상 이렇게 손으로 지워줘야 합니다.
}

객체에 대한 포인터를 여러 컨테이너에 넣어두었다고 생각해 봅시다. 그렇다면, 각 컨테이너마다 해당하는 포인터가 존재하는지 죄다 확인해가면서 삭제해야하는 우울함이 발생합니다. 흐흑.

shared_ptr을 써보면..

#include<vector>
#include <boost/shared_ptr.hpp>
class Test; // 역시나 있다고 칩시다.
typedef boost::shared_ptr<test> TSP;
typedef std::vector<tsp> TSPVector;
TSPVector vec;
void test7_init()
{
// vec에 새 객체를 추가합니다. 카운트는 1이지요.
vec.push_back(TSP(new Test));
}

void test7()
{
// init()에서 추가한 객체가 파괴됩니다. 카운트가 0가 되니까요.
vec.pop_back();
}

직접 지워주지 않아도 shared_ptr의 파괴자에서 알아서 객체를 파괴시켜줍니다. 지긋지긋한 메모리누수현상에서 해방이지요. 하핫. 여러 컨테이너에 넣는 경우도 전혀 우울하지 않습니다. 한군데서 지운다고 다른 컨테이너에서 문제가 발생하진 않을테니까요. 카운트가 0가 되는 시점에서 파괴된다는 컨셉은 메모리관리에서 아주 행복한 컨셉이지요.


3. shared_ptr의 파괴동작.

shared_ptr을 단순히 new/delete를 이용해 생성/파괴하는 객체에만 사용할 수 있다는 생각은 크나큰 오산입니다. Custom Destructor를 세팅할 수 있게 해주는 기능은 shared_ptr의 가능성을 대폭 증가시켜 줍니다. 🙂

#include <boost/shared_ptr.hpp>
// 사실 싫어하는 예이지만, HANDLE을 예로 들고 싶기에 어쩔수.. 없;;
#include <windows.h>
typedef boost::shared_ptr<void> HANDLESP;

void test8()
{
HANDLE h = ...(뭐든 핸들을 받아온다)...
HANDLESP hsp = HANDLESP(h, CloseHandle);
}// hsp가 파괴될때 CloseHandle(h)가 호출된다.

hsp를 생성할때 보면, 뒤쪽에 CloseHandle이란 함수를 넣어주는 것을 발견할 수 있습니다. CloseHandle의 위치에는 HANDLE(정확히는 void*)을 매개변수로 받는 호출가능한 C++객체는 무엇이든 올 수 있습니다. 🙂

즉, 객체가 C++ 표준인 new/delete를 이용해 할당되지 않더라도 파괴될때 호출할 호출가능한 객체를 지정해주면, delete대신 그 함수를 통해 객체를 파괴하게 되지요.

DLL 사이에서 객체를 주고 받을 때도 매우 유용합니다. DLL A에서 생성한 객체를 DLL B에서 파괴할 경우 문제가 발생하기 때문에, A의 인터페이스에 객체를 삭제하는 함수를 등록시켜서 쓰는 것이 일반적인데, 이런 경우에도 객체를 삭제하는 함수를 파괴시 호출할 함수로 지정해주면 간단히 shared_ptr을 적용할 수 있는 것이지요. 이때, 전에 설명했던 boost::bind가 큰 힘을 발휘하는 경우가 많답니다. 🙂


4. Tip.

shared_ptr이 가진 좋은 점은, C++ 프로그래머를 할당/해지, 혹은 생성/파괴 관리라는 리소스 누수현상에서 해방시켜준다는 점입니다. 상당히 행복하지요. 🙂
조금 더 행복해 지기 위해서 간단한 팁을 하나 준비했습니다.

class A
{
...(some definitions)...
public:
typedef boost::shared_ptr<a> SP;
}

정말 간단합니다. class를 하나 만들때마다 SP라는 이름으로 shared_ptr을 미리 정의해 주는 것인데요, 매번 boost::shared_ptr<타입이름>을 치기 귀찮으니 미리미리 typedef을 해두자는 간단한 작전입니다. 일단 해보면 실제 작업할때 상당히 편하기도 하고, 타입이름*보단 타입이름::SP를 통해 좀 더 안전한 코드를 미리 작성하게 되는 부수효과도 있답니다-


A. References

shared_ptr 문서
Effective STL, Scott Meyers 저/곽용재 편역, Addison Wesley / 인포북


B. notes

2009년 5월 31일: 워드프레스에 맞게 재편집.
2009년 11월 25일: 스크롤바 문제로 재편집.

boost::*를 배워봅시다에 관해.

C++를 밥벌이로 삼은지 1년하고도 9개월 정도 되었습니다. (병특이 1년 하고도 3개월 남았다는 이야기네요. 하아)
그간, 이런 저런 삽질을 하다가 STL을 만났고 boost를 만났습니다만, STL의 강력하지만 모자란 부분을 훌륭히 매꿔주는
boost의 강력함에 매료되어 이곳 저곳에서 사용하기 시작했고, 이 강력함을 좀 널리 알려보고자 시리즈를 쓰고자 마음을
먹었습니다.
감히 한번 써보자고 마음을 먹었습니다.

그 1탄은 boost라이브러리중 가장 큰 임팩트를 남기며 감동을 주었던 boost::bind입니다.
smart_ptr, format, variant, lambda, pool, spirit등의 라이브러리를 다뤄볼 생각이구요.
여력이 된다면, python도 다룰 생각입니다. (boost에 포함되지는 않았지만 luabind도 다룰 생각입니다)

그 외에도 제가 만들어서 사용하는 유틸리티들도 다룰 계획인데, 이건 간간히 다루게 될 것 같군요.

STL/Boost의 강력함. 느껴보세요. 🙂

boost::bind를 배워봅시다.

1. 멋진 예제.

C++에서 코딩을 하다보면, 다음과 같은 코드를 실행할 일이 종종 생기곤 한다.

//무식한 코드
class A
{
public:
    void Start();
    int SomeWork(int,int,int);
};

std::vector<a> v;
...(some inits)...

for(std::vector<a>::iterator itr = v.begin();
     itr != v.end(); ++itr)
{
    // itr은 포인터가 아니므로 이게 더 맞는 표현이라고 한다..
    (*itr).Start();
}

vector에 담긴 class A의 instance에 대해서, Start 메소드를 일괄적으로 호출하는 경우인데, 이런 처리를 boost라이브러리의 bind(이하 boost::bind)를 사용하면 효과적으로 처리할 수 있다. (타이핑 수가 준다)

//이쁜코드.
#include <boost/bind.hpp>
class A
{
public:
    void Start();
};

std::vector<a> v;
... (add to v) ...
std::for-each(
    v.begin(),
    v.end(),
    boost::bind(&A::Start, _1)
);

언뜻 보아도, 2개의 bracket(중괄호)와 std::vector<A>::iterator라는 긴 타입선언이 사라졌다. 키햐.

2. 인수 고정과 자리표.

boost::bind는 기본적으로 여러개의 인수를 갖고 있는 호출 가능한 C++ 객체(함수, 함수자-operator()를 구현한 struct혹은 class)의 인수들을 고정시키는 역할을 한다. 예를 들어, 인수로 a,b,c를 가진 함수를 반복적으로 호출할때, 3개의 인수중 2개를 고정시키고 하나만 바꿔가며 호출해야하는 상황에 유용한 라이브러리이다.

//간단한 예제
int x_minus_y(int x, int y)
{
    return x-y;
}
// same as x_minus_y(x,3);
x_minus_3 = boost::bind(x_minus_y, _1, 3);

위의 예제는 주어진 정수 x,y를 빼서 그 결과를 반환하는 x_minus_y라는 함수의 2번째 인자를 3으로 고정시킨 호출가능 객체(함수자)인 x_minus_3을 리턴하고 있다. 2번째 인자가 3으로 고정되었기 때문에, x_minus_3을 호출할 때는 다음과 같이 정수 하나만 넘겨주면 된다.

//호출 예제
x_minus_3(2); // 결국은 minus(2,3)과 동일한 효과를 갖는다.

x_minus_3을 생성하기 위해 사용한 구문에서 보면, _1이라는 특이한 표현을 사용하고 있는데, 이것이 자리표(placeholder)이다. 자리표는 bind에 의해 생성된 함수자가 받을 인수를 의미한다. _1은 첫번째로 받을 인수, _2는 두번째, _n은 n번째의 인수가 되겠다.

이를 응용하면 x_minus_y를 이용해, y에서 x를 뺀 결과를 넘겨주는 y_minus_x를 만들 수도 있다.

//y_minus_x
y_minus_x = boost::bind(x_minus_y, _2, _1);

위의 구문이 생성한, y_minus_x는 2번째로 받은 인수(_2)를 첫번째에 넣고, 첫번째로 받은 인수(_1)을 두번째에 넣어 x_minus_y를 호출해 준다. 이 자리표를 이용하면, 상당히 많은 응용을 할 수 있게 된다.

물론, 자리표를 사용하지 않고, 인수를 받지 않는 함수자를 생성할 수도 있다.

3_minus_2 = boost::bind(3_minus_2, 3, 2);

3_minus_2의 호출 결과는 언제나 1이 될 것이다.

3. 멤버함수와 bind

멤버함수를 bind와 사용하기 위해서는 boost::mem_fn이란 라이브러리에 대한 이해가 필요하다. 간단히 예제를 통해 알아보면,

//1절에서 사용한 class A를 그대로 쓰겠습니다아-
a_somework_functor = boost::mem_fn(&A::SomeWork);
A a_instance;
// same as a_instance.SomeWork(1,2,3);
a_somework_functor(a_instance, 1, 2, 3);

위와 같은 구문에 의해 생성된 a_start_functor함수자는 첫번째 인수로 주어지는 A의 인스턴스를 이용해 SomeWork을 호출하게 된다. C++에서 인스턴스와 멤버함수의 포인터를 이용해 함수를 호출하는 정식 구문이 없고, 이를 구현하기 위해서는 operator .*나 operator .->를 활용해야 하는데, 이 작업이 생각보다 복잡하므로, 이러한 함수자의 형태로 편리하게 이용하는 것이다. (자세한 사항은 Modern C++ Design 5장 참고)

1장에서 사용했던 예제를 다시 가져와서 살펴보자.

std::for-each(v.begin(), v.end(), boost::bind(&A::Start, _1));

boost::bind가 사용자의 편의성을 고려해주지 않았다면 boost::bind(&A::Start, _1) 대신 boost::bind(boost::mem_fn(&A::Start), _1)을 사용했어야 할 것이다. 하지만, 영리한 boost::bind는 첫번째 인수로 멤버함수가 올 경우 알아서 boost::mem_fn을 이용해 처리를 해준다.

boost::bind(&A::Start, _1)에 의해 생성된 함수자는 첫번째 인수로 A의 인스턴스를 넣어주어야 한다. 즉, boost::bind(&A::Start, _1)(a)는 a.Start()와 같은 의미를 갖게 되는 것이다.

std::for-each 구문은 첫번째와 2번째 인수로 주어진 iterator의 범위 내에서 각각의 요소에 대한 참조를 3번째 인수로 주어진 함수자를 이용해 호출한다. (STL 관련 서적 참고) 따라서 v에 들어있는 모든 a의 인스턴스에 대해 멤버함수인 A::Start()를 호출하게 된다.

4. 합성 함수.
수학 시간을 되돌려 보자. f(g(x))라는 수식이 기억 나는가? 그렇다. g(x)의 결과를 이용해 f(x)에 대입시켜 그 결과를 사용하는 합성함수이다. boost를 사용하면 C++에서 합성함수를 쉽게 만들 수 있다.

//합성함수예제
int f(int x)
{
    return x+3;
}

int g(int y)
{
    return (-1)*y;
}

g_f_x = boost::bind(g, boost::bind(f, _1)); // same as g(f(x))
f_g_x = boost::bind(f, boost::bind(g _1)); // same as f(g(x))

g_f_x 함수자를 만들때 사용된 bind의 예를 보면, g의 첫번째 인수를 boost::bind(f, _1)로 주고 있다. 이는 boost::bind(f, _1)의 결과를 첫번째 인수로 주겠다는 의미가 되며, 이는 우리가 원하던 합성함수이다!

합성함수를 잘 이용하면 세상이 편해진다.

struct Item{
   int score();
   Summary get_summary();
};

struct Summary
{
    bool check();
};

std::vector<item>; v;

v에 들어있는 Item중 get_summary().check()의 결과가 true가 되는 Item의 인스턴스를 찾는 문제를 생각해보자. 이런 경우에 bind가 또 한번 괴력을 발휘한다.

//합성함수를 이용한 찾기 예제.
std::find_if(
    v.begin(),
    v.end(),
    boost::bind(
        &Summary::check,
        boost::bind(Item::get_summary, _1)
    )
);

여러 줄로 나눠서 썼지만, 실은 한줄이다! get_summary(_1).check()를 bind를 이용해 표현하면 이렇게 되는 것이다.

5. 정렬.

boost 1.33에 업데이트된 bind는 <, >, ==와 같은 기초 비교 연산자를 지원한다. 이게 무슨 소리인가 싶지만, 예제를 보면 감이 올 것이다.

std::sort(
    v.begin(),
    v.end(),
    boost::bind(&Item::value, _1) < boost::bind(&Item::value, _2)
);
&#91;/cpp&#93;
v에 들어있는 Item들을 Item::value의 결과값을 이용해 정렬하는 예제이다. 만세! bind에 의해 생성된 함수자에 operator<를 오버로딩 하여, <가 있을 경우 비교하는 함수자를 자동으로
생성해 주는 기능! 현재, ==, !=, <=, >=, <, > 를 지원한다고 하니 매우 편리할 듯 싶다.

좀 더 알아보면,

[cpp]
std::find_if(
    v.begin(),
    v.end(),
    boost::bind(&Item::value, _1) == 3);

위와 같이 Item::value()의 결과값이 3인 요소를 찾는 것도 쉽게 가능하다.

6. 결론

클래스와 클래스들의 컨테이너, 그리고 STL의 강력한 알고리즘을 사용하는데 상당한 애로사항이 존재했던 것이 사실이다. 매번 함수자를 만들어 작업해야 했던 옛날과 비교하면 행복할 따름이다. boost::bind는 STL의 진정한 강력함을 느끼기 위해 필수적으로 사용해야 하는 라이브러리라고 생각된다. (다행히 STL확장과 관련된 Draft에 보면 이런 형태의 bind가 기본으로 들어올 듯 싶다)

또한, bind와 함수자를 적절히 잘 이용한다면, 콜백과 같은 기능을 구현하는데 매우 유용할 수 있다. 또, Command패턴을 이용하는데도 매우 유연하게 적용될 수 있다.

A. boost::function
예제에서 사용한 boost::bind의 결과물 들은 타입을 지정하지 않은 채 변수에 대입되고 있다. 귀찮아서 생략한 것이고, 실제로는 타입을 지정해줘야 하는데, 그 타입은 boost::function을 사용하면 쉽게 할 수 있다. boost::function은 함수자를 정의하는 라이브러리라고 생각해도 무방하며, 선언된 변수에는 signature가 맞는 어떤 호출가능한 객체라도 넣을 수 있으니 참고하시길.

쓰는 법은 다음과 같다.

// 리턴값이 없고, 인수가 int 하나인 함수 f1
boost::function<void (int)> f1;
boost::function<int (double, std::string&)> f2;

// int를 리턴하고, double과 std::string의 참조를 받는 함수 f2

다만, VC6와 같은 오래된 컴파일러에서는 구문이 다소 달라질 수 있으므로 참고하시길.

B. 참조
boost에서 함수자와 관련된 라이브러리를 사용하다보면, 종종 위험한 상황이 종종 발생한다.

std::string a = "AAAAAA";
// a의 복사본이 전달된다!
boost::bind(some_function, a);
// a의 레퍼런스가 전달된다!
boost::bind(some_function, boost::ref(a));

C++의 특성상 발생하는 문제라고 보여지는데, bind 템플릿을 전개할때, 참조로 호출해야하는 상황에 복사본이 전달되는 상황이 발생할 수 있다. 이를 해결하기 위해, boost::ref가 존재한다. 그냥 쉽게 boost::ref를 안쓰면 전부 call-by-value이고, 쓰면 call-by-reference라고 생각하시면 되겠다.

C. References

boost의 함수관련 문서들
Modern C++ Design, Andrei Alexandrecu, Addison Wesly (이기형 역, 인포북 발간)

ps. 피드백 부탁 드립니다.
ps2. 불펌 금지입니다.
ps3. 워드프레스로 옮겼더니 깨지더군요. 그래서 수정.
ps4. 멤버함수 관련된 부분이 수정되었습니다. A::Start라고 쓰면, 표준에 어긋나더군요. &를 붙여서 함수주소를 넘기는 방법이 정확한 방식입니다.

프로그래머의 3대 덕목.

회사 인트라넷에 대충 올렸던 걸 다시 정리해서..말하자면 재활용입죠. 하하.

세계에서 가장 많이 쓰이는 스크립트 언어중 하나인 perl을 만든 Larry Wall아저씨는, laziness가 프로그래머의 덕목이라고 주장을 합니다아. 맞는 말이죠. Randal Schwartz라는 사람은 Camel Book에서 3대 덕목으로 확장시켜서 주장을 합니다..

원문 : Virtues of a Perl Programmer

펄 프로그래머의 덕목이라고 되어있지만, 실은 모든 프로그래머에게 적용되는 사항인듯 합니다.

머리아프다고.. 영어라고.. 저 쪽에서부터 돌이 날아오는 소리가 들리기에 대충 요약 해보지요.


1. Laziness – 게으름 혹은 귀차니즘.

에너지 소모를 줄이기 위한 노력을 하게끔 하는 녀석입니다. 요는 프로그래밍을 할 때 최대한 노동량을 줄이기 위한 방향으로 짠다는 것이지요. 예를 들면 자주쓰는 루틴 같은 것은 라이브러리로 빼놓는 다 던지, 상속관계를 잘 사용해서 타이핑 수를 줄이거나 생각해야 하는 량을 줄인다던지 하는 것들입니다. 이런 식의 작업을 하다보면, 모듈화/라이브러리화가 진행되기 마련이지요..

모듈화/라이브러리화가 진행되면 다른 사람들도 사용하게 됩니다. – 열린소스이든 닫힌소스이든 마찬가지지요. 혼자 코딩하는 것이 아니라면 말이죠.. – 이렇게 될 경우, 작성한 프로그래머는 수없이 많은 질문의 바다에 빠져 허우적대기 시작합니다. 그럼 그 질문들이 귀찮아서 문서화를 하게 되지요. 한번의 투자로 반복을 하지 않는 것. 이것이 게으름 혹은 귀차니즘 입니다. 멋지지 않나요?

2. Impatience – 참을성 없음

컴퓨터가 못놀게 하는 겁니다. 그때 그때 필요한 코드만 작성하는 것이 아니라, 앞으로 필요해질 것들까지 작성하는 것이지요. 꼭 작성하지 않더라도 최소한 준비는 하는 겁니다. 확장성이라든지 기타 등등.

할 수 있을때 미리미리 하거나, 아니면 미리미리 준비하는 참을성 없음. 참 아름다운 덕목입니다. 어찌보면, laziness의 확장판이라고도 할 수 있지요.

3. Hurbis – 자만

하늘을 찔러 구멍을 내고 바다를 찍어 마르게 할 정도의 자존심! 감히 단점이 언급되지 않을 정도의 프로그램을 작성하게 하는 자기만족! 더 말할 나위 없이 세번째!


참 글을 재밌게 쓰는 사람들 입니다. 하하.
전적으로 동의하게 만드는 사항들이지요. 하아. 언제쯤이면 3번째를 만족시키며 살 수 있을까나..

C++ 서적들.

0. The C++ Programming Language


C++의 창시자인 Bjarne Stroustrup옹이 친히 집필하신 바이블. 최고다. TCPP와 맞먹음.
특정 상황에서 C++의 작동이 궁금하거나, 표준이 궁금하다면 끼고 살아야함. (실제로 끼고 삼)


1. C++ Standard Library 튜토리얼-레퍼런스


조슈티스 책이라고도 불리는 STL에 관련된 최고의 레퍼런스.
적절한 설명과 적절한 팁과 적절한 예제가 잘 범벅이 되어있음.


2. Effective STL/C++, More Effective C++


특히 Effective STL. 정말 유용한 책. Meyers아저씨 원츄.
주로 팁으로 이루어져 있는 것 같지만, 각 항목들이 유기적으로 잘 구성되어 있다. 헤헷.
지하철에서 읽기 좋은 시리즈.


3. C++ 코딩의 정석


Herb아찌랑 Andrei아찌랑 쿵짝쿵짝해서 쓴, 가이드라인. 현재 유행하고 또 권장되는 C++스타일과 유의사항들을 명료하게 볼 수 있는 사전식 책.


4. Modern C++ Design


Template에 대한 새로운 지평. C++ 템플릿을 제대로 쓰려면 이 책은 꼭 읽어봐야 한다.
결과보다는 과정을 중시했다는 느낌의 책.


5. C++ Template Metaprogramming


Boost에 대해 갖게되는 막연한 두려움을 어느정도 해소시켜주는 책.
– 사실 boost는 metaprogramming 말고, 일반적으로 많이 쓸만한 라이브러리를 모아서 정리한 책이 필요하긴 하다.


6. Efficient C++


최근 담당하는 제품 최적화 작업에 들어가면서, 가장 많은 도움이 되는 책. C++최적화 기법들과 기법들을 적용할때 유의할 점들이 정리되어 있다.

7. Design Patterns: Elements of Reusable Object-oriented Software


GoF책이라고 불리우는
명저.(4명의 저자를 일컬어 Gang of Four라고 불렀고.. 줄여서 GoF라고 한다…) C++서적이라고 보기는 힘들수도
있지만, 책의 예제들이 C++로 나온다. 히힛. Design Pattern이 뭔지 알게해준 고마운 책. 원서다.. 으흑. 게다가
제본이다. 조만간 사야겠다… (학부때 가난하던 시절에… ;;;)

etcs..

이거 외에도 몇권 있기는 한데..
Essential C++/Accelerated C++은 이미 알고 있는 내용이었으므로 패스.
C++ 프로그래밍의 이해. / C프로그래머를 위한 C++ (한빛문화사-오라일리) 는 사실 입문서라서 패스. – 정말 입문서다. 내용이 약하다.

ps. 개인적인 생각으로 C++을 MFC로 시작해서 배우는 것 만큼 우울한 것은 없다. MFC는 하나도 안 멋있고, 매혹적이지도 않다. 회사 사장님식 표현에 의하면 “sexy한 스펙이 아니다.”

ps2. 오랜만에 긴거 썼다.

ps3. 사진의 소스는 강컴과 Yes24. 허락받지 않아 죄송하고, 소스제공 ㄳ

구글 데스크톱.

정말 최고로 편하다. 으아 ㅡㅜ

최근 정신없이 바빠서 산출물을 어디에 뒀는지도 깜빡깜빡하는데다가..
어떤 정보를 메일에 적어놨는지, 디스크에 적어놨는지 헷갈리기도 하고.
유용한 정보가 있는 사이트를 까먹기도 하고. (웹기록 검색)
등등등…

구글 데스크톱 한방으로 해결.
으으으 초 감동. ㅡㅜ

뭔가 찾는데 걸리는 시간은 단연코 1/10!!!
– 물론 본인이 정리를 잘 안하긴 함;;; –

ps. 저같이 정신없는 사람을 위한 소프트웨어인듯. 이힛.

들뢰즈와 programming.

(들뢰즈라기 보단 이진경이긴 한데.. -_-;)

노마디즘을 다시 읽고 있는 요즘, template을 이용한 Generics와 유사한 점을 발견. 순간 기뻤다.

"기계" 라는 개념과 "template"라는 기능이 의외로 유사한 점을 보여주는데, 이는 배치와 접속에 따라 달라지는 무언가를 공유하고 있다는 느낌.

template는 형과 상관 없이 어떤 알고리즘이나 구조를 서술하는데는 적격인 녀석이다. int이든 double이든 float이든 (심지어는 string이든!!) 상관하지 않고 적용될 수 있는 알고리즘 같은 것 말이다.

이런 유사성을 발견하고 나서, 생각해보니 programming에 들뢰즈씨의 멋진 개념들이 적용될 수 있는 것은 무엇일까 라는 생각이 들었다.

생각의 선은 주욱 타고 올라가, 한때 논란거리였던 마이크로 커널과 모놀리딕 커널까지 올라가게 되었다. 이힛. 마이크로 커널은 좀
자율적인 모듈(프로세스)들이 서로 메시지를 주고 받으며 자신이 해야할 최소한의 역할만 수행하면서 유기적으로 연결되어 작동하는
일종의 리좀(스러운 무언가)이고, 모놀리딕 커널은 그런 분화없이 서로 동일한 공간내에서 tight하게 연결되어 제어되는 일종의
수목(스러운 무언가) 라고 생각된다. 결론은 어떻게 났냐고? 마이크로 커널로 개발되던 GNU/Hurd와 모놀리딕 커널로 개발되던
Linux의 양대 오픈소스 커널이 어떤 길을 걸었는지 생각해보면 쉽게 나온다. 모놀리딕이 이겼다.

그렇다고 해서, 오랜만에 찾아낸 멋진 단초를 버릴 수는 없다. 요즘 필요한 것이 flexible한 framework이고, 이
framework를 설계하는데 중심사상(?)으로 사용될 무언가를 발견하고자 하는 의지. 이게 산다는 것이지. 우훗.

"기관없는 신체"와 연결되어 프로그래머의 욕망을 실현시킬 수동적인 버젼의 리좀. 욕망의 실현을 위해 전부 재작성 하지 않고,
단지 배치만 바꾸어 원하는 바를 이룰수 있는 framework. 많은 사람들이 꿈꿔온 것이고, 사례도 많지만, COM+나
EJB나 기타 등등 다른 것들이 현재 요구사항을 만족시키지 못하는 것도 사실이니…

한 2주정도 고민해봐야겠다. 🙂

Activity Based Programming

회사동료인 ica씨와 이야기하다 3년 전에 만들어둔 개념이 문득문득 불끈불끈 솟아올라서 정리해보기.

Entity
Flow를 타고 흐르는 시작에서 끝까지의 Instance를 의미.
단위 트랜잭션으로 볼 수도 있고, 단위 컨텍스트로 볼 수도 있다. 기본적으로는 인풋이 발생할때마다 하나씩 생성된다.
공장으로 치면 컨베이어 벨트위에 있는 가공중인 제품이다.

Activity
Entity를 받아서 가공하고 넘겨주는 최소로직의 단위.
공장으로 치면 각각의 작업자.

Resource
Activity에서 사용하는 시스템 자원들.
역시나 공장으로 치면 작업자들이 쓰는 공구.

Flow
Entity가 어떤 Activity를 거쳐서 지나가는지에 관한 정의.
공장으로 치면 컨베이어 벨트다.

사실 이런 개념을 만드는데 영향을 준 Business Process나 Manufacturing Process에서는 모든걸 Resource로 보는 경향이 있긴 하지만, 이건 생각하기 나름인거 같다.

이런 방식은 보통 프로그래밍을 할때 생각하는 방식을 좀 벗어난 것으로, 실제로 적용된다면 프로그램의 수정/유지보수가 상당히
간단해질 듯 하다. 사실 OOP라는게 너무 광범위 하고 사람별로 멋대로 짤 수 있는 상황인 반면에 OOP를 이용해 ABP를
적용하면 틀(?)이 좀 더 쉽게 잡히지 않나 싶다.

내가 생각해낸게 그렇지만, 누군가 미리 해놓은걸꺼다. 🙁
오랜만에 Research나 해볼까? ㅋㅋ

최근 즐겨쓰기 시작한 C++ Template등을 이용해 만들면 이쁘지 않을까.. 라는 생각이 들어버렸다.