복잡한 캐스팅 콜백 함수 단계에서 일어나는 것을 방지하기 위해 템플릿을 도입해 보았다.
#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.