Allegro/STL Tutorial Ch.5 Study+more

Chapter 5

5. STL Basics

5.1 C++ Templates

Another thing that C++ offers is the concept of templates. In many ways, templates have many things in common with macros, but are processed by the C++ compiler itself, with the stronger type checking this implies, instead of the preprocessor. As an example, consider the old C macro
C++이 제공하는 특징중에 또다른 하나는 템플릿 이다. 다양한 방법으로 템플릿은 매크로가 하는일을 할 수 있다. 더불어 C++에서 전처리기가 아닌(매크로는 전처리기에서 처리됨) 컴파일러를 통해 처리되고 타입 체킹이 강화되었다.
보통 C 매크로는 다음과 같다.

#define MAX(x,y) (((x)>(y))?(x):(y))

For simple cases, such as MAX(a,b) and even MAX(a+b,c*d), this will work fine, but what if we do MAX(a++,b++)? This is implementation-specific, but at least one of the parameters is likely to be incremented twice. Also, there is no type checking, which could cause problems with more complex macros, or confusing error messages. This is just a few of the drawbacks of using macros. Another common implementation is
간단히 MAX(a,b) 와 MAX(a+b, c*d)는 잘 처리가 된다. 하지만, MAX(a++, b++) 은 어떻게 되지? 이것은 구현 스펙에 따라 달라지긴 하지만 아마도 두 변수중에 최소한 한 변수는 두번 ++ 가 될것이다. 게다가, 매크로는 타입 체킹도 없다. 여기에 더해서, 복잡한 매크로는 알수없는 에러메세지나 알수 없는 에러의 원인이 될 수 있다. 매크로에는 이런 약점이 존재한다. 다른 일반적인 구현은 다음과 같다.

inline int max(int a,int b) { return a>b?a:b; }

which does not suffer from the above drawbacks, but now will only support integers, and a new max() would have to be written for any other data type you might want to use. Even though they could all be overloaded, it would be tedious, and still not work for user-defined types.
이것은 앞서 매크로의 약점과 같은것은 없다. 하지만 이것은 단지 int 만 지원한다. 다른 데이터 타입을 지원하기 위해서는 다른 함수를 또 작성해야 한다. 물론, 오버로드를 통해서 동일한 함수명으로 만들수 있지만, 귀찮다. 또한, 만약 유저가 새로 정의한 타입으로는 동작하지 않는 문제가 있다. (응? 유저가 정의한 타입으로 동작하지 않는다는게 무슨 말임? 동작 안하나???)
C++ templates solves all of these problems. You can simply define max() for a templated abstract data type T, then the C++ compiler will automatically instantiate the template for any data types that is actually used.
C++ 템플릿은 이러한 문제들을 모두 해결했다. 당신은 단순히 max()를 템플릿화된 추상 데이터 타입인 T로 선언하면 된다. 그러면 C++ 컴파일러는 템플릿화된 함수를 당신이 실제로 사용하는 데이터 타입으로 초기화자동 변환 해 줄것이다.

template <class T>
inline T max(T a,T b) { return a>b?a:b; }

We now have a pretty perfect max() definition. However, in our game we're currently not really interested in defining template functions, but rather in finding a way to manage a list of sprite objects. Fortunately, templates can also be used for class data types. Here is an example of a template class that can store any data type.
이제 우리는 완벽한 max() 를 만들었다. 하지만, 실제로 우리 게임에서는 이런 템플릿 함수를 선언하는것은 별 의미가 없다. 그 대신 스프라이트 오브젝트를 관리하는 방법을 찾아야 한다. 다행이도, 템플릿은 클래스 데이터 타입에도 사용할 수 있다. 여기, 어떤 데이터 타입도 저장할 수 있는 템플릿 클래스 예제가 있다.

template <class T>
class storage {
  public:
    T data;
    storage(T dat) { data=dat; }
    T getdata() const { return data; }
};

The const in there means that the function does not have any side effects, and can safely be called for const (read-only) objects.
const 는 함수가 어떤 부작용도 없다는것, 그리고 const(읽기 전용의) 객체를 안전하게 호출할 수 있다는 의미이다. (이거 완전히 직역인데, 누가 의역좀 해주세연. 공부가 짧아서 무슨 의미인지 잘 이해가 안되네요;)
However, when we use such a template class, we must tell which data type the template should be instantiated for, like this:
하지만, 우리가 템플릿 클래스를 사용할때, 우리는 템플릿이 어떤 데이터 타입으로 초기화 되어야 하는지 지정해 주어야 한다. 다음의 예를 보자.

storage<int> my_int(200);
cout << my_int.getdata() << endl;

This will, of course, print 200 to the screen. You can think of the instantiation process as replacing every occurrence of T in the class definition with the appropriate type, int in this case. This way, you can make a template class work with practically any data type you want. So what we are going to do, is to let STL work with our sprite base class.
이것은 물론, 화면에 200을 찍는거다. 충분히 예상되겠지만, 클래스 선언에 있는 T가 적절한 타입으로 교체된다. 위의 경우에서는 int 가 되겠다. 이런식으로 당신은 템플릿 클래스를 사용해서 원하는 데이터 타입으로 작업을 할 수 있는것이다. 그래서(...?) 이제 우리가 할일은 우리의 sprite 베이스 클래스에 STL을 적용해 보는것이다.

5.2 The Standard Template Library

The Standard Template Library, or STL, is a collection of template classes and template functions to provide a general-purpose data-management system with extreme flexibility without sacrificing efficiency. This is, of course, very convenient for us, since we need to manage a list of game objects in an efficient way. However, this library does have a steep learning curve, and easily confuses beginners. So let's take a brief overview first, then show how it can be used in our game.
Standard Template Librayr, 다시말해 STL은 효율성을 희생하지 않으면서도 일반적인 목적의 (유연한) 데이터 관리 시스템을 위한 템플릿 클래스및 템플릿 함수들의 집합이다. 이것은, 즉, 우리가 게임에서 사용할 오브젝트를 효율적으로 관리할 목적에 충분히 부합하는것이다. 하지만, 이 라이브러리는 학습 곡선이 심하게 경사져있다. 한마디로 초보자들은 상당히 헤멜것이다. 그러니 간단히 훑어보고, 우리 게임에서 어떻게 쓰이는지를 보여주겠다.
The STL provides a wide variety of container classes. We are just going to use sequences, of which STL provides these:
STL은 다양한 컨테이너 클래스들을 제공해 준다. 우리는 STL이 제공해주는 다음과 같은것들을 사용할 것이다:

vector, essentially a resizeable array
벡터, 동적 사이즈의 배열.
deque, similar to a vector but can quickly add and remove elements at the beginning and end of the array
데크, 벡트와 비슷하지만 양쪽에서 추가 삭제가 가능함.
list, a double-linked list, can quickly add and remove elements anywhere
리스트, 더블 링크드 리스트, 리스트 어디에 있는 element라도 빠르게 추가/삭제가 가능함.
slist, a single-linked list, faster than list but one-way
싱글 리스트, 싱글 링크드 리스트, 리스트 보다 빠름, 하지만 한쪽 방향으로만 가능.
For a game, where objects come and go at will, it seems most reasonable to use the "list" template class, so that's what we are going to use. So, let's see how we are going to use it to store pointers to sprite objects:
게임에는, 어디로든지 데이터를 집어 넣었다가 삭제할수 있는 "list" 템플릿 클래스가 쓸모가 있어 보인다. 따라서, 우리가 앞으로 사용할것은 리스트 템플릿 클래스이다. 그러니, 스프라이트 오브젝트 포인터를 관리하기 위해서 어떻게 하는지 다음의 코드로 이해해 보자.

#include <allegro.H>
#include <stl.H>
#include "tutorial.h"

#define MIN_Y 8

DATAFILE*data;
BITMAP*backdrop,*framebuf;

class sprite {
  protected:
    fix X,Y;
  public:
    sprite(fix _X,fix _Y) { X=_X; Y=_Y; }
    virtual ~sprite() {}
    virtual void draw(BITMAP*dest) {}
    virtual void animate() {}
};

typedef list<sprite*> sprite_list;

sprite_list sprites;

class chopper : public sprite {
  public:
    chopper(fix _X,fix _Y) : sprite(_X,_Y) {}
    virtual void draw(BITMAP*dest) {
      draw_rle_sprite(dest,(RLE_SPRITE*)data[TUT_CHOPPER].dat,X,Y);
    }
    virtual void animate();
};

void chopper::animate()
{
  if (key[KEY_LEFT]||joy_left) --X;
  if (key[KEY_RIGHT]||joy_right) ++X;
  if (key[KEY_UP]||joy_up) --Y;
  if (key[KEY_DOWN]||joy_down) ++Y;
}

class chopper2 : public chopper {
  public:
    chopper2(fix _X,fix _Y) : chopper(_X,_Y) {}
    virtual void animate();
};

void chopper2::animate()
{
  if (key[KEY_LEFT]||joy_left) ++X;
  if (key[KEY_RIGHT]||joy_right) --X;
  if (key[KEY_UP]||joy_up) ++Y;
  if (key[KEY_DOWN]||joy_down) --Y;
}

int main()
{
  allegro_init();
  install_keyboard();
  initialise_joystick();

  data=load_datafile("tutorial.dat");

  set_gfx_mode(GFX_VGA,320,200,0,0);

  set_palette((RGB*)data[TUT_GAMEPAL].dat);

  // create 320x192 backdrop
  backdrop=create_bitmap(320,192);
  for (int Y=0; Y<128; Y++) hline(backdrop,0,Y,319, (Y/2)+128);
  for (int Y=128; Y<192; Y++) hline(backdrop,0,Y,319, ((Y-128)/2)+192);

  // create 320x200 double buffer
  framebuf=create_bitmap(320,200);
  clear(framebuf);

  chopper Hero(50,100);
  chopper2 AnotherHero(250,50);
  sprites.push_back(&Hero);
  sprites.push_back(&AnotherHero);

  while (!key[KEY_ESC]) {
    // build frame
    blit(backdrop,framebuf,0,0,0,MIN_Y,320,200);
    // draw sprites
    {
      sprite_list::const_iterator spr=sprites.begin();
      while (spr!=sprites.end()) {
        (*spr)->draw(framebuf);
        spr++;
      }
    }
    // display frame
    vsync();
    blit(framebuf,screen,0,0,0,0,320,200);
    // animate sprites
    poll_joystick();
    {
      sprite_list::const_iterator spr=sprites.begin();
      while (spr!=sprites.end()) {
        (*spr)->animate();
        spr++;
      }
    }
  }
  return 0;
}

Let's go through what exactly it is we are doing here. First, we #include <stl.H>, which should be fairly obvious what for. This is really a convenience header that includes all other STL header, it's not really part of STL itself. We could include the exact headers we need, especially if we wanted to reduce compilation time, or make our programs more portable, but for simplicity, we'll just use stl.h.
뭐가 어떻게 되는 소스인지 이해해보자. 우선, 우리는 stl.h 를 include했다. 이는 STL 관련 헤더들을 모두 포함하는 메타 헤더이다. 즉, 정확히는 stl.h 는 STL의 일부분이 아닌것이다. 컴파일 타임이 오래걸리거나 포터블한 코드를 만들고 싶다면 필요로 하는 헤더만 정확히 include하면 된다. 하지만, 그런건 귀찮으니까, 우리는 단순히 stl.h 를 include할 것이다.
Next, we use typedef to define sprite_list as the instantiation of the list template class we are going to use. This is not strictly necessary, but it's easier to remember and perhaps change later, than to use list<sprite*> all over. Then we define the global variable sprites to be a variable of this type; that is, it will be our sprite list.
다음으로, 우리는 텀플릿 클래스의 인스턴스를 typedef를 사용하여 sprite_list 로 만들었다. 이것은 꼭 필요한 구문은 아니지만, 기억하기 쉽고 나중에 코드를 고치기도 쉽게 만들어 준다. 맨날 list<sprite*> 를 치는건 좀 삽 아님? 어쨌든, 우리는 전역 변수를 이 타입으로 선언했고, 이 변수가 우리의 스트라이트 리스트가 될 것이다.

In the main code, we use the push_back() method to insert our helicopter object as the last element in the sprite list. We could also use push_front() instead to insert it as the first. We also insert two helicopter objects, to show that it actually works.
메인 코드에서, push_back() 메쏘드를 사용하여 헬리콥터 오브젝트를 스프라이트 리스트의 마지막에 삽입한다. 물론 push_front() 를 사용해도 되긴 한다. 실제적인 예를 위해서 우리는 두개의 헬리콥터 객체를 삽입했다.

In the main game loop, we traverse the linked list twice, once to draw the sprites onto the double buffer, and once more to move them about. In each of these traversals, we need a variable that tells us where in the list we are. This is what an iterator does. Note that in C++ you can define types inside a class, and STL has conveniently defined the appropriate iterator types iterator and const_iterator inside the class, so we can define an iterator to traverse the list. The ++ operator of the iterator moves it to the next element. The * (dereferencing) operator of the iterator returns the element itself, which happens to be of the type sprite*.
메인 게임 루프에서, 링크드 리스트를 두번씩 돌면서 한번은 더블 버퍼에 스프라이트를 그리고, 또 한번은 스프라이이트를 움직이게 한다. 리스트를 도는 과정에서 우리는 변수가 리스트의 어디즘에 있는 변수인지 알 필요가 있다. 이것을 iterator가 해준다. 참고로 C++에서는 클래스 내부에서 타입을 디파일 할수 있고, STL은 iterator와 const_iterator라는 이너레이터 타입들을 제공해 준다. 따라서 우리는 iterator를 디파인해서 리스트를 돌(traverse)수 있다. iterater에 ++ 연산자는 다음 엘레멘트로 이동하도록 해 준다. *(역참조) 연산자는 해당하는 sprite* 타입의 엘레멘트를 리턴해 준다.

As you can see, the power of STL is what we just did, to abstract a complex data structure into something as simple, flexible and manageable as this, even if it does take some effort to learn.
당신이 봤다시피, STL은 복잡한 데이터 구조체를 추상화하여 간단하고, 확장성 있으면서도 관리하기 편하게 만들어 준다. 쵸큼 배움의 노력이 필요하지만, 배울 가치는 충분하다고 생각 안혀?

To learn more, proceed to the next chapter
이제 점점 흥미진진해 지는데?- .-? 라고 생각하면 다음 챕터를 보도록 하자 : )

refer: http://www.ping.uio.no/~ovehk/allegro/tut5.html
우웅, 시간이 남을때 또 번역을 계속해 보자. 이거 뭐.. 학교에서 다 배웠던거 같은데, 다시보니 하나도 모르겠고
이런 단기기억 상실증이라니.. 내가 내가!! 아악!!

번역을 다 끝내면 가든에 올려야지 ㅎㅎ;

이글루스 가든 - 하루에 한 장~ 꾸준히 번역하기

트랙백

이 글과 관련된 글 쓰기 (트랙백 보내기)
TrackbackURL : http://handmade.egloos.com/tb/5072577 [도움말]

덧글

덧글 입력 영역