Home // Blog
Home // Notice
Home // Tag Log
Home // Location Log
Home // Media Log
Home // GuestBook
Inter-thread communication in C++ #7
Posted at 2008. 11. 5. 00:12 //
in S/W개발/C++ 이야기 //
by
ITCThread의 설계상에 심각한 버그가 있었더군요. 다음과 같은 부분이 잘못 됐었습니다.
void doIt(void (Callee::*mf)(void))
{
doIt(CallType(boost::bind(mf, (Callee*)this)));
}
void doIt(void (Callee::*mf)(void))
{
doIt(CallType(boost::bind(mf, (Callee*)this)));
}
template <typename ArgType1, typename ArgType2>
void doIt(void (Callee::*mf)(ArgType1, ArgType2),
ArgType1 arg1, ArgType2 arg2)
{
doIt(CallType(boost::bind(mf, (Callee*)this, arg1, arg2)));
}
위 코드를 보면 this를 Callee* 로 무작정 casting을 하고 있는데, 이렇게 할 경우 다음과 같은 방식을 처리하는 것이 불가능하더군요.
struct Base
{
virtual void print()
{
cout << "Base::print()" << endl;
}
};
struct Derived : public Base
{
virtual void print()
{
cout << "Derived::print()" << endl;
}
};
struct BaseITCAdaptor : public ITCThread<Base>
{
BaseITCAdaptor(Base* b, const string& name) :
ITCThread<Base>(b, name)
{}
void print()
{
doIt(&Base::print);
}
};
Base* b1 = new Base();
Base* b2 = new Derived();
BaseITCAdaptor bitcthr(b1, "baseitcthr");
BaseITCAdaptor ditcthr(b2, "deriveditcthr");
bitcthr.print();
ditcthr.print();
bitcthr.quit(), ditcthr.quit();
bitcthr.join(), ditcthr.join();
delete b1;
delete b2;
즉, ITCThread 별로 polymorphic 하게 member function을 호출하는 것이 불가능했다는 것이죠. 이러한 사용예를 가능하게 하기 위해 ITCThread를 다음과 같이 수정했습니다.
template <typename Callee, typename PolicyType = WaitAndExecute>
class ITCThread
{
public:
ITCThread(Callee* callee, const std::string name = "itcthr") :
m_callee(callee), m_thr(), m_policy(new PolicyType),
m_name(name), m_run(true), m_code(0)
{
m_thr = boost::thread(&ITCThread::run, this);
}
......
private:
......
Callee* m_callee;
......
};
그리고, doIt member function들은 다음과 같이 수정했습니다. 이전에는 (Callee*)this 와 같이 강제 형변환을 수행했던 부분을 m_callee 멤버 변수를 활용하여 수정했습니다.
void doIt(void (Callee::*mf)(void))
{
assert(m_callee != 0);
doIt(CallType(boost::bind(mf, m_callee)));
}
template <typename ArgType>
void doIt(void (Callee::*mf)(ArgType), ArgType arg)
{
assert(m_callee != 0);
doIt(CallType(boost::bind(mf, m_callee, arg)));
}
template <typename ArgType1, typename ArgType2>
void doIt(void (Callee::*mf)(ArgType1, ArgType2),
ArgType1 arg1, ArgType2 arg2)
{
assert(m_callee != 0);
doIt(CallType(boost::bind(mf, m_callee, arg1, arg2)));
}
template <typename ArgType1, typename ArgType2, typename ArgType3>
void doIt(void (Callee::*mf)(ArgType1, ArgType2, ArgType3),
ArgType1 arg1, ArgType2 arg2, ArgType3 arg3)
{
assert(m_callee != 0);
doIt(CallType(boost::bind(mf, m_callee, arg1, arg2, arg3)));
}
FunctionITCAdaptor는 다음과 같이 수정했습니다.
template <typename PolicyType = WaitAndExecute>
struct FunctionITCAdaptor : public ITCThread<Nothing, PolicyType>
{
FunctionITCAdaptor(const std::string& name) :
ITCThread<Nothing, PolicyType>(0, name) {}
};
// example #3
s = "hello";
Hello* h = new Hello();
HelloITCAdaptor hello(h, s);
for (int i = 0; i < 1000; ++i)
{
hello.sayHelloTo("Yoonsoo Kim");
}
hello.quit();
hello.join();
delete h;
// example #4
s = "btn_handler";
ButtonHandler* bh = new ButtonHandler();
ButtonHandlerITCAdaptor btnHdlr(bh, s);
btnHdlr.doIt(&ButtonHandler::onClick, 10, 20);
btnHdlr.quit();
btnHdlr.join();
delete bh;
XXXITCAdaptor 형태의 예제는 초기화 인자로 호출대상이 되는 클래스 인스턴스를 넣어줘야 됩니다. ITCThread에서 상속 받아서 wrapping member function을 작성하는 경우에는 다음과 같이 생성자를 작성해 주면 됩니다.
class HelloThread : public ITCThread<HelloThread>
{
public:
HelloThread(const string& name = "hello") :
ITCThread<HelloThread>(this, name) {}
......
};
class PrintVector : public ITCThread<PrintVector>
{
public:
PrintVector(const string& name = "prvec") :
ITCThread<PrintVector>(this, name)
{}
......
};
이상은 설계 및 구현상의 버그를 수정한 것이고, 이 외에 다음과 같은 수정 사항이 있습니다.
void doIt(void (Callee::*mf)(void))
{
doIt(CallType(boost::bind(mf, (Callee*)this)));
}
void doIt(void (Callee::*mf)(void))
{
doIt(CallType(boost::bind(mf, (Callee*)this)));
}
template <typename ArgType1, typename ArgType2>
void doIt(void (Callee::*mf)(ArgType1, ArgType2),
ArgType1 arg1, ArgType2 arg2)
{
doIt(CallType(boost::bind(mf, (Callee*)this, arg1, arg2)));
}
위 코드를 보면 this를 Callee* 로 무작정 casting을 하고 있는데, 이렇게 할 경우 다음과 같은 방식을 처리하는 것이 불가능하더군요.
struct Base
{
virtual void print()
{
cout << "Base::print()" << endl;
}
};
struct Derived : public Base
{
virtual void print()
{
cout << "Derived::print()" << endl;
}
};
struct BaseITCAdaptor : public ITCThread<Base>
{
BaseITCAdaptor(Base* b, const string& name) :
ITCThread<Base>(b, name)
{}
void print()
{
doIt(&Base::print);
}
};
Base* b1 = new Base();
Base* b2 = new Derived();
BaseITCAdaptor bitcthr(b1, "baseitcthr");
BaseITCAdaptor ditcthr(b2, "deriveditcthr");
bitcthr.print();
ditcthr.print();
bitcthr.quit(), ditcthr.quit();
bitcthr.join(), ditcthr.join();
delete b1;
delete b2;
즉, ITCThread 별로 polymorphic 하게 member function을 호출하는 것이 불가능했다는 것이죠. 이러한 사용예를 가능하게 하기 위해 ITCThread를 다음과 같이 수정했습니다.
template <typename Callee, typename PolicyType = WaitAndExecute>
class ITCThread
{
public:
ITCThread(Callee* callee, const std::string name = "itcthr") :
m_callee(callee), m_thr(), m_policy(new PolicyType),
m_name(name), m_run(true), m_code(0)
{
m_thr = boost::thread(&ITCThread::run, this);
}
......
private:
......
Callee* m_callee;
......
};
그리고, doIt member function들은 다음과 같이 수정했습니다. 이전에는 (Callee*)this 와 같이 강제 형변환을 수행했던 부분을 m_callee 멤버 변수를 활용하여 수정했습니다.
void doIt(void (Callee::*mf)(void))
{
assert(m_callee != 0);
doIt(CallType(boost::bind(mf, m_callee)));
}
template <typename ArgType>
void doIt(void (Callee::*mf)(ArgType), ArgType arg)
{
assert(m_callee != 0);
doIt(CallType(boost::bind(mf, m_callee, arg)));
}
template <typename ArgType1, typename ArgType2>
void doIt(void (Callee::*mf)(ArgType1, ArgType2),
ArgType1 arg1, ArgType2 arg2)
{
assert(m_callee != 0);
doIt(CallType(boost::bind(mf, m_callee, arg1, arg2)));
}
template <typename ArgType1, typename ArgType2, typename ArgType3>
void doIt(void (Callee::*mf)(ArgType1, ArgType2, ArgType3),
ArgType1 arg1, ArgType2 arg2, ArgType3 arg3)
{
assert(m_callee != 0);
doIt(CallType(boost::bind(mf, m_callee, arg1, arg2, arg3)));
}
FunctionITCAdaptor는 다음과 같이 수정했습니다.
template <typename PolicyType = WaitAndExecute>
struct FunctionITCAdaptor : public ITCThread<Nothing, PolicyType>
{
FunctionITCAdaptor(const std::string& name) :
ITCThread<Nothing, PolicyType>(0, name) {}
};
// example #3
s = "hello";
Hello* h = new Hello();
HelloITCAdaptor hello(h, s);
for (int i = 0; i < 1000; ++i)
{
hello.sayHelloTo("Yoonsoo Kim");
}
hello.quit();
hello.join();
delete h;
// example #4
s = "btn_handler";
ButtonHandler* bh = new ButtonHandler();
ButtonHandlerITCAdaptor btnHdlr(bh, s);
btnHdlr.doIt(&ButtonHandler::onClick, 10, 20);
btnHdlr.quit();
btnHdlr.join();
delete bh;
XXXITCAdaptor 형태의 예제는 초기화 인자로 호출대상이 되는 클래스 인스턴스를 넣어줘야 됩니다. ITCThread에서 상속 받아서 wrapping member function을 작성하는 경우에는 다음과 같이 생성자를 작성해 주면 됩니다.
class HelloThread : public ITCThread<HelloThread>
{
public:
HelloThread(const string& name = "hello") :
ITCThread<HelloThread>(this, name) {}
......
};
class PrintVector : public ITCThread<PrintVector>
{
public:
PrintVector(const string& name = "prvec") :
ITCThread<PrintVector>(this, name)
{}
......
};
이상은 설계 및 구현상의 버그를 수정한 것이고, 이 외에 다음과 같은 수정 사항이 있습니다.
- ITCAdaptor 삭제
- ITCThread::quit()을 default 인자를 활용하여 하나로 통합
- ITCThread::doQuit()을 인자가 하나인 것만 유지하고 종료 코드값을 내부적으로 유지
- ITCThread::join() 이 quit()호출시 지정했던 종료 코드를 리턴하도록 수정
- ProcessingPolicy 라는 인터페이스 클래스를 삭제하고, WaitAndExecute, ProcessAndExecute와의 상속 관계를 없앰.
- WaitAndExecute, ProcessAndExecute 의 구현 내용을 itc.h 파일로 모두 옮김