복잡한 캐스팅 콜백 함수 단계에서 일어나는 것을 방지하기 위해 템플릿을 도입해 보았다.
#include <iostream>
// 전역변수 콜백 함수 포인터.
// 어떤 라이브러리 어딘가에 있다고 가정한다.
void (*g_callback)() = NULL;
// callback과 상속 관계가 없는 데이터 저장 클래스
class storage
{
public:
storage() {}
~storage() {}
int& val() { return val_; }
const int& val() const { return val_; }
private:
int val_;
};
// 정적변수만을 취급하는 기본 클래스를 선언하였다.
template <typename T>
class callback_base
{
protected:
callback_base() {}
~callback_base() {}
public:
static void set_class(T* _ptr)
{
T** base_ptr;
base_ptr = get_base_ptr();
(*base_ptr) = _ptr;
}
// 이중 포인터 표현을 감출 수 있다.
// 어떤 것을 사용하는지는 사실 사용자의 취향에 따라 달라질 수 있다.
inline static T& get_class() { return **get_base_ptr(); }
protected:
// static 변수를 함수 안에 삽입하였다.
// 그렇지만 엄연히 cb는 존재한다.
static T** get_base_ptr()
{
static T* base;
return &base;
}
};
class callback_class : public callback_base<storage>
{
// cb_base의 직접적인 생성은 금지되어 있다.
protected:
callback_class() {}
~callback_class() {}
public:
// 라이브러리가 필요로 하는 콜백 함수
// 상위 클래스에서만 스태틱 변수를 관리하고
// 상위 클래스를 상속받아 콜백 함수를 새롭게 정의한다.
// storage 클래스는 사용자가 직접 만들어야 할 변수이다.
static void callback()
{
// 멤버 변수를 조작할 수 있도록 클래스를 얻는다.
// 구질구질한 포인터 참조 연산자가 사라졌다.
storage& real = get_class();
std::cout << "member variable n: " << real.val() << std::endl;
}
};
int main(int argc, char** argv)
{
// 일반적인 데이터 저장 클래스이다.
// 복수 개를 선언하여 여러 콜백이 동시에 동작할 수 있는지 확인한다.
storage s1, s2;
s1.val() = 10;
s2.val() = 20;
// 같은 static 함수를 callback하되
g_callback = &callback_class::callback;
// 때때로 다른 데이터를 선택하도록 한다.
// 템플릿 인자로 데이터 타입이 명시되어 가독성이 높아지는 부가적인 효과를 얻었다.
callback_class::set_class(&s1);
g_callback();
callback_class::set_class(&s2);
g_callback();
return EXIT_SUCCESS;
}
템플릿을 사용하여 클래스를 사용할 수 있는 콜백 형태를 만들었다. 하지만 여전히 몇몇 문제가 남아 있을 것이다. 그 중 “다양한 데이터 클래스들의 콜백 함수를 다룰 때는 그 다양한 인자를 어떻게 콜백 함수로 전달할 것인가?”라는 문제는 직감적으로 다가온다.
일단, 내 기본적인 아이디어는 콜백 함수에서 스태틱 포인터 변수를 활용하여 데이터 관리 객체의 비정적 멤버 변수에 접근하는 방법을 제안하는 것이며, 실제로 이 아이디어는 나 혼자만의 아이디어는 아님을 밝혀 둔다. 또한 이 방법이 최선이라고는 말할 수 없다. 더 훌륭한 방법들이 존재할 것이다.
어쨌든 C-Style의 콜백 함수가 그다지 C++에 걸맞는 구조가 아님에도 불구하고,
이런 방법을 통해 콜백 함수에서 클래스를 사용할 수 있다는 것을 제안함에 의의를 둔다.
참조:
CPUBitmap Class, J. Sanders, E. Kandrot, ‘CUDA by Example’, Addison Wesley.