'Command Pattern'에 해당되는 글 1건

  1. 2008.07.26 Command Pattern - 일반화 구현 by 1stPasa

커맨드 패턴을 Function 과 Method 에 대해 일반화 하고

인수타입과 리턴타입에 대해서도 일반화 하였다.

한글이름도 어려운 명시적 템플릿 구체화까지 써가면서도

인수타입이 void 인경우에 대해 NoArg란 이름을 붙여 따로 정의할수밖에 없었던 이유는

생각지 못했던 Ambigious 문제가 발생했기 때문이다. ( 겪고보니 당연한 일이었지만.. )

결국 클래스의 종류는

Command ( 상위 클래스 )
MethodCommand, MethodCommandNoArg
FunctionCommand, FunctionCommandNoArg

다섯가지나 된다.. 사용할때 올바른 인스턴스를 만들어주는게 번거로우므로

MakeCommand 란 함수를 만들었는데 이것역시 템플릿 함수임에도 불구하고 4가지 버전이나 된다.

그놈의 void가 문제지.. return type 으로도 안되고 Argument로 넣을수도 없으니 별도로 구체화 시킬수밖에..ㅋ

아래는 그렇게 구현한 Command 패턴들과 MakeCommand 코드이다.

#pragma once
template <typename _A, typename _R>
class Command
{
public:
 virtual _R Fire(_A) = 0;
};

template<typename _T, typename _A, typename _R>
class MethodCommand : public Command<_A,_R>
{
 _T *m_obj;
 _R (_T::*m_mf)(_A);
public:
 MethodCommand(_T * object, _R (_T::*method)(_A)) : m_obj(object), m_mf(method) {}
 virtual inline _R Fire(_A arg){ return (m_obj->*m_mf)(arg);}
};

template<typename _T, typename _A>
class MethodCommand<_T,_A,void> : public Command<_A,void>
{
 _T *m_obj;
 void (_T::*m_mf)(_A);
public:
 MethodCommand(_T * object, void (_T::*method)(_A)) : m_obj(object), m_mf(method) {}
 virtual inline void Fire(_A arg){(m_obj->*m_mf)(arg);}
};

template <typename _T, typename _R>
class MethodCommandNoArg : public Command<void,_R>
{
 _T *m_obj;
 _R (_T::*m_mf)();
public:
 MethodCommandNoArg(_T * object, _R (_T::*method)()) : m_obj(object), m_mf(method) {}
 virtual inline _R Fire(void){ return (m_obj->*m_mf)();}
};

template <typename _T>
class MethodCommandNoArg<_T,void> : public Command<void,void>
{
 _T *m_obj;
 void (_T::*m_mf)();
public:
 MethodCommandNoArg(_T * object, void (_T::*method)()) : m_obj(object), m_mf(method) {}
 virtual inline void Fire(void){ (m_obj->*m_mf)();}
};

template <typename _A,typename _R>
class FunctionCommand : public Command<_A,_R>
{
 _R (*m_mf)(_A);
public:
 FunctionCommand(_R (*func)(_A)) : m_mf(func){}
 virtual inline _R Fire(_A arg){ return (*m_mf)(arg); }
};

template <typename _A>
class FunctionCommand<_A,void> : public Command<_A,void>
{
 void (*m_mf)(_A);
public:
 FunctionCommand(void (*func)(_A)) : m_mf(func){}
 virtual inline void Fire(_A arg){ (*m_mf)(arg); }
};

template <typename _R>
class FunctionCommandNoArg : public Command<void,_R>
{
 _R (*m_mf)(void);
public:
 FunctionCommandNoArg(_R (*func)(void)) : m_mf(func){}
 virtual inline _R Fire(void){ return (*m_mf)(); }
};

template <>
class FunctionCommandNoArg<void> : public Command<void,void>
{
 void (*m_mf)(void);
public:
 FunctionCommandNoArg(void (*func)(void) ) : m_mf(func){}
 virtual inline void Fire(void){ (*m_mf)(); }
};

template <typename _T, typename _A, typename _R>
Command<_A,_R>* MakeCommand( _T *target, _R (_T::*method)(_A) )
{
 return new MethodCommand<_T,_A,_R>( target, method );
}

template <typename _T, typename _R>
Command<void,_R>* MakeCommand( _T *target,  _R (_T::*method)(void) )
{
 return new MethodCommandNoArg<_T,_R>( target, method );
}

template <typename _A, typename _R>
Command<_A,_R>* MakeCommand( _R (*function)(_A) )
{
 return new FunctionCommand<_A,_R>( function );
}

template < typename _R>
Command<void,_R>* MakeCommand(_R (*function)(void) )
{
 return new FunctionCommandNoArg<_R>( function );
}

Command<_A , _R>  
-  _A : 인수 타입
-  _R : 반환 타입

간단하게 시작했는데 100줄을 넘겨버렸다.. ㅎㅎ

아래는 예제코드로서

void 와 void가 아닌 타입들을 대입할수 있다고 생각하면 되겠다.

위에서 말한것과 같이 void 의 경우 return 타입으로 쓸수 없기 때문에

return func1(); 과 같은 형태로 사용할수도 없고.

func1( arg ); 와 같은형태로 인수로 넣을수도 없다.

#include "Command.h"
#include <iostream>
using namespace std;

class MyClass
{
public:
 void method1()
 {
  cout << "void Method(void)" << endl;
 }

 int method2(int a)
 {
  cout << "int Method(int)" << endl;
  return a;
 }

 int method3(void* a)
 {
  cout << "int Method(void*)" << endl;
  return 10;
 }

 void method4(int a)
 {
  cout << "void Method(int)" << endl;
 }
};

void func1()
{
 cout << "void function(void)" << endl;
}

int func2(int a)
{
 cout << "int function(int)" << endl;
 return a;
}

int func3(void* a)
{
 cout << "int function(void*)" << endl;
 return 10;
}

void func4(int a)
{
 cout << "void function(int)" << endl;
}


int main(int argc, char* argv[])
{
 MyClass a;

 Command<void,void> *m1 = MakeCommand(&a, &MyClass::method1 );
 Command<int,int> *m2 = MakeCommand(&a, &MyClass::method2 );
 Command<void*,int> *m3 = MakeCommand(&a, &MyClass::method3 );
 Command<int,void> *m4 = MakeCommand(&a, &MyClass::method4 );
 
 Command<void,void> *f1 = MakeCommand(func1);
 Command<int,int> *f2 = MakeCommand(func2);
 Command<void*,int> *f3 = MakeCommand(func3);
 Command<int,void> *f4 = MakeCommand(func4);
 
 m1->Fire();
 m2->Fire(10);
 m3->Fire(NULL);
 m4->Fire(10);

 f1->Fire();
 f2->Fire(10);
 f3->Fire(NULL);
 f4->Fire(10);

 return 0;
}

생각같아선 C#의 델리게이트처럼 인수의 갯수도 일반화 시켜버리고 싶지만..

아무리 생각해도 인라인어셈을 동원하지 않고는 구현이 불가능한것 같다.

그렇게되면 컴파일타임 에러체크가 안되므로 패스하고

결국 이쯤에서 마무리를 짓는것이 좋을듯.
Posted by 1stPasa

댓글을 달아 주세요