GilGil 님의 글을 보고 영감을 얻어 구현해 보았습니다.

MetaClass 를 이용해 인스턴스 생성시 대상 클래스의 생성자 타입 의존성을 없애려는 시도인데 템플릿이 좀 과한듯도 싶네요.


어찌되었건. 인자가 한개건 두개건( 지금은 최대 2개까지만 구현했습니다 ) 혹은 디폴트생성자건 마음대로 골라서

메타클래스에게 클래스 생성을 위임할수 있도록 구현하는데 목적을 두었습니다

일반적인 팩토리 패턴의 구현과는 다소 다른데요.. 설명하자니 길어서 우선 소스부터 적어버려야 겠네요

struct Point
{
 int x;
 int y;
};   class Shape
{
public:
 virtual void Draw() = 0;
};   class Circle : public Shape
{
 public:
  Circle(){printf("Circle Def\n");}
  Circle(bool visible){}
  Circle(Point,int){}
  Circle(Point,Point){printf("Circle Two pts\n");}
  virtual void Draw(){}
};   class Ellipse : public Shape
{
 public:
  Ellipse(){printf("Ellipse Def\n");}
  Ellipse(bool visible){}
  Ellipse(Point,int){}
  Ellipse(Point,Point){printf("Ellipse Two pts\n");}
  virtual void Draw(){}
};   class Triangle : public Shape
{
 public:
  Triangle(){printf("Triangle Def\n");}
  Triangle(bool visible){}
  Triangle(Point,Point,Point){}
  virtual void Draw(){}
};   class Rectangle : public Shape
{
 public:
  Rectangle(){printf("Rectangle Def\n");}
  Rectangle(bool visible){}
  Rectangle(Point,Point){printf("Rectangle Two pts\n");}
  virtual void Draw(){}
};


이런 클래스 들이 있다고 가정했을때

아래와 같이 이용할 수 있습니다.


typedef Constructor<> DefaultConstructor;
typedef Constructor<bool> BooleanConstructor;
typedef Constructor<Point,Point> TwoPointConstructor;   typedef DefaultConstructor::MetaClassList DefMetaList;
typedef TwoPointConstructor::MetaClassList TwoPtsMetaList;   REGISTER_METACLASS( Circle, Shape, DefaultConstructor )
REGISTER_METACLASS( Ellipse, Shape, DefaultConstructor )
REGISTER_METACLASS( Triangle, Shape, DefaultConstructor )
REGISTER_METACLASS( Rectangle, Shape, DefaultConstructor )   REGISTER_METACLASS( Circle, Shape, TwoPointConstructor )
REGISTER_METACLASS( Ellipse, Shape, TwoPointConstructor )
REGISTER_METACLASS( Rectangle, Shape, TwoPointConstructor )
 
int main()
{
 int i;    DefMetaList *def_ml = VMetaClassMap::getMetaClassList<DefaultConstructor>("Shape");
 TwoPtsMetaList *two_ml = VMetaClassMap::getMetaClassList<TwoPointConstructor>("Shape");    if( def_ml )
 {
  DefMetaList::iterator it;
  for(it = def_ml->begin() ; it != def_ml->end(); it++)
   Shape *sh = (Shape*)(*it)->getFactory()->construct();
 }
  
 if( two_ml )
 {
  TwoPtsMetaList::iterator it;
  Point pt;     for(it = two_ml->begin() ; it != two_ml->end(); it++)
   Shape *sh = (Shape*)(*it)->getFactory()->construct(pt,pt);
 }  
 return 0;
}

템플릿 클래스를 사용하기 때문에 사실 카테고리도 "Shape" 와 같은 문자열이 아니라 실제 타입 단위로 지정할 수 있지만

굳이 그렇게 구현하지는 않았습니다. (그런데 글을 쓰면서 보니 그것도 상당히 좋은 기능이라는 생각이 드네요.. ^^; )


또한, 생성자의 매개변수를 템플릿 인자로 받는 Constructor 가 구분자(?) 처럼 사용되기 때문에

예제에서 보인 클래스들 처럼 부모가 같은 클래스가 아니어도 하나의 카테고리로 묶어버릴수 있다는 장점이 있죠.

서로 전혀 상관이 없는 클래스들 일 지라도 단순히 같은 형태의 생성자만 보유하고 있다면

같은 카테고리로 묶어두었다가 리스팅 하고, 인스턴스를 생성할 수 있습니다.


몇년전에 작성했던 Functor 를 기반으로 팩토리 패턴을 나름 확장하고

나름 의미있다고 생각했던 문자열 기반 categorization 정도만 구현한 코드라고 할 수 있겠네요

재미삼에 업무시간에 짬짬히 한거라... 코드가 영 깔끔치 못해도 아이디어 공유차원의 코드니까 이해해 주세용


DeligatingConstruct.tar


아 참고로 g++ version 4.4.3 을 이용했습니다

Posted by 1stPasa

댓글을 달아 주세요


Plain Old Data(POD)

C의 struct나 built-in-type 과 동일한 메모리 구조를 갖는 object를 의미.
memset, memcpy에 의한 단순 메모리 복사가 가능한 메모리 구조를 갖는다.
POD 의 조건은 다음과 같다

1. built-in type
2. 가상 함수가 없고 사용자 정의 할당자와 소멸자를 가지지 않은 class의  object
3. non POD를 non-static 멤버로 가지지 않은 class의 object

가상함수가 존재할경우 추가정보가 필요하므로 Non POD 임이 명백하지만
할당자와 소멸자를 갖는 Class 의 경우 할당자와 소멸자의 존재 자체가 메모리 구조를 바꿀이유는 없다
하지만 할당자와 소멸자의 경우

class NPOD
    {
    int x_;
    char* buf_;

    NPOD() : buf_(0) {}
    ~NPOD() { delete buf_; }
    NPOD& operator=(const NPOD& other)
        {
        x_ = other.x_;
        int bufLen = strlen(other.buf_);
        buf_ = new char[bufLen+1];
        memcpy(buf_, other.buf_, bufLen);
        }
    }

NPOD t1, t2;
...
t1 = t2;
memcpy(&t1, &t2);



위와 같은 경우 memcpy 의 결과가 t1=t2 의 결과와 같지 않게 된다.
따라서 규격에서는 할당자와 소멸자가 유저에 의해 정의되어있는 Class 는 Non POD 로 정의한다.

마지막으로 static 멤버의 경우 object에 포함되는것이 아니기 때문에 Non POD 이건 POD 이건 상관이 없다.
따라서 POD가 되려면 non static 멤버로는 POD만을 가져야만 한다.


출처
http://gpgstudy.com/forum/viewtopic.php?t=10148&view=previous

Posted by 1stPasa

댓글을 달아 주세요

  1. Richardml 2014.12.24 05:37  댓글주소  댓글쓰기 수정/삭제

    이용약관위배로 관리자 삭제된 댓글입니다.

Widget *w = new Widget();

위와같이 클래스를 동적할당하면 C++은 두단계를 거쳐서 Widget의 인스턴스를 만들어 낸다.

우선 operator new 를 거쳐 메모리를 allocation 하는 작업이 선행되는데

코드를 살펴보면 단순히 malloc 을 사용해 메모리를 할당만 해줄 뿐이다.

사용자는 이 operator new 를 오버로딩 함으로써 특정 타입의 메모리 allocation을 컨트롤 할수 있다.

하지만 이대로는 Widget의 생성자가 호출이 되지 않은, 쓰레기만 차있는 메모리일 뿐인데

이 공간에 의미를 부여해주는것이 ( 그래봤자 생성자를 호출해줄 뿐. )

new operator 의 역할이다.


new operator는 건네받은 메모리 공간을 this 포인터로 밀어넣고

생성자를 호출해줌으로써 클래스 구실을 할수 있도록 만들어준다.

생성자 역시 하나의 ( this call type 의 )멤버 함수이므로  메모리에

인스턴스화 되지 않은채로는 호출할 방도가 없다.


Widget::Widget() 이런식으로 호출할수도 없는노릇이니..

생성자를 호출해주고 사용가능한 메모리 공간으로 만들어주는것은 인라인 어셈을 쓰지 않고서는

언뜻 c++ 문법만으로는 불가능해 보인다

하지만 아래와 같이 가능하다

new ( (void*) ptr ) Widget( );


위의 코드는 ptr 이 가리키는 메모리 공간을 필두로 Widget 클래스의 생성자를 호출해준다.

생성자의 인수에 따라 복사생성자를 포함한 어떠한 생성자도 호출이 가능하다

위의 코드를 디스어셈블리 해보면 operator new 가 한번 호출되는것을 확인할수 있는데.

그때의 operator new는 메모리를 할당하는것이 아니고 단지 전달받은 ptr을 돌려주기만 할 뿐이다.

그냥, 아무 하는일 없는 operator new가 한번 호출된다고 생각하면 된다.

Posted by 1stPasa

댓글을 달아 주세요