Developfordummies 01

깊이있는 삽질 Ubuntu Korea Community Wiki
이동: 둘러보기, 검색

문제 발생[편집]

  • 이거 프로그램을 짜서 돌리면 제대로 돌아가기나 할까?

문제점 분석[편집]

  • 아주 기본적인 프로그램을 작성하여 돌려보도록 하자.

해결 방안[편집]

  1. 그냥 단순한 텍스트가 출력되는지 보자.
  2. 그냥 단순한 창 같은 거 하나 띄워보자.
  3. 그냥 단순한 텍스트 나열을 보자.
  • 사실 Hello World라는건 동작 확인에 쓰이는 것이다.
  • 내가 프로그래밍을 해서 Hello World가 화면에 찍히기까지 많은 일이 준비되어 있다는 거다.
  • 프로그래머야 그냥 쉽게 확인한다고 생각하지만, 여러 가지가 전제되어 있는 환경이 이미 구축되어 있는 것이다.
  • 하지만 프로토타입 설계할 때 그런 건 신경쓰지 말자. 괜시리 설계만 쓸데없이 커진다.
  • 사실 Hello World의 경우 굳이 프로그래밍할 때 순서도를 작성하지 않아도 되는 유일한 프로그램일 것이다.
  • 일반적인 경우 무조건 플로챠트를 그리길 권한다.
  • 물론 이 문서에서 다음 챕터부터는 플로챠트가 꼭 등장한다.
  • 먼저, 인터넷에서(검색엔진이 무엇이 되었든) Hello World를 검색한다.
  • Hello World 어플리케이션은 대부분의 언어에서 샘플로 제공할 것이다. 그걸 쓰면 된다.
  • 가장 많이 이야기되는 C언어를 보자.
#include <stdio.h>
int main(int argc, char* argv[])
{
  printf("Hello World!\n");
  return 0;
}
  • 잘 보면 printf가 핵심이라는걸 알 수 있을 것이다.
  • 하지만 요즘 세상에 누가 Console 화면을 볼 것인가?
  • (물론 필자나, 리눅스 유저들은 Console 화면을 좋아하지만, 대부분의 유저들은 좋아하지 않는다)
  • 그러므로 뭐 Window라도 하나 띄워보는 것이 좋겠다.
  • Window를 띄우는 함수는 Windows의 MFC를 사용하도록 하겠다.
#include <afxwin.h> // include MFC library

//
// CHelloView is an subclass of MFC CWnd class which prints "Hello World!" into the center of its window instance.
//
class CHelloView : public CWnd
{
public:
    CHelloView() {};
    virtual ~CHelloView() {};

    // An callback function that draws the content of the window into the given device context.
    // One of the alternatives to this method may be OnPaint. However, OnDraw is recommended since it supports being sent to the printer.
    void OnDraw(CDC* dc)
    {
        // Here, via the 'TextOut' function, we draw the <s>in</s>famous Hello, world! message into the center of the window.
        CRect rect;
        GetClientRect(&rect); // Queries size and position of client area of the window into 'rect' variable.
        dc.TextOut(rect.right/2 - 40,rect.bottom/2 - 10,"Hello, world!"); // This assumes the text is 80 pixels wide and 40 pixels tall, but this needs some workaround since many different system provides different default font faces.
    }        
    DECLARE_MESSAGE_MAP() // A message map is required for every window class to override the window procedure.
};

//
// A main window frame which places 'Hello, world!' view inside itself.
//
class CMainFrame : public CFrameWnd
{
public:
    CMainFrame() {};
    virtual ~CMainFrame() {};

protected:    
    CHelloView m_wndView;

    // Overrides WM_CREATE.
    int OnCreate(LPCREATESTRUCT lpCreateStruct)
    {
        // OnCreate requires call of parent function
        if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1;
        
        // Create a child window which will eventually occupy entire area of the main frame.
        if (!m_wndView.Create(NULL, NULL, AFX_WS_DEFAULT_VIEW,
            CRect(0, 0, 0, 0), this, AFX_IDW_PANE_FIRST, NULL)) return -1;
        
        return 0;
    }

    virtual BOOL OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)
    {
        // Pass every WM_COMMAND message to our child window.
        if (m_wndView.OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
            return TRUE;
    
        // Use default handling if the child window has nothing to do with it.
        return CFrameWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
    }        
    DECLARE_MESSAGE_MAP() // A message map is required for every window class to override the window procedure.
};

//
// Application is the entry point of almost every single MFC program
//
class Application : public CWinApp
{
public:
    Application() {};
    
    virtual BOOL InitInstance() // Initalize the app instance. Called by MFC.
    {        
        // This creates a main window object which will be set to be this application's main window.
        CMainFrame* pFrame = new CMainFrame;
        m_pMainWnd = pFrame;

        // Creates, and shows the main frame window.
        pFrame->Create(NULL,"Hello World Application");
        pFrame->ShowWindow(SW_SHOW);
        return TRUE;
    };
};

BEGIN_MESSAGE_MAP(CHelloView,CWnd) // leave this blank as we don't have any messages to process. Default implementation does all the thing.
END_MESSAGE_MAP()

BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
    ON_WM_CREATE()    // Maps WM_CREATE message to our custom callback.
END_MESSAGE_MAP()

// Create out global application object
// Notice we have no WinMain, because the MFC library 
// implements one for us
Application theApp;
  • 자, 그냥 하나 띄우는데에도 코드가 엄청 커졌다는걸 느낄거다. (사실 그냥 메인 윈도우에서 그리면 될 걸 굳이 클래스 또 만들어서 그런 탓도 크다)
  • 그러니까 책들에서 웬만하면 GUI가 안 나오는거다..
  • 예전에는 저걸 전부 외우거나 책을 보고 타이핑을 했어야 하지만, 요즘은 그렇게 프로그래밍하면 효율적이지 않다는 이야기를 듣게 될 것이다.
  • 각 컴파일러에서 제공하는 예제를 보고, 어느 부분이 핵심인지 파악하는게 중요하다.
  • 즉, 여기서는 CreateWM 부분이 중요하며, MFC는 먼저 윈도우를 띄우고 나서 내용을 채워넣는 방식이라는걸 알 수 있다.
  • 의외로, 많은 책들이 코드를 보는 방법에 대해서는 잘 가르쳐주지 않는 것 같다.
  • WinApi 를 쓰면 MFC보다는 실행 속도가 빠를 수 있겠지만 여전히 짧지 않다.
#include <windows.h>
#include <tchar.h>

LRESULT CALLBACK WndProc (HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam) {
    HDC hdc; PAINTSTRUCT ps;
    switch (nMsg) {
    case WM_PAINT:
        hdc = BeginPaint (hWnd, &ps);
        TextOut (hdc, 0, 0, _T("Hello, world!"), 13);
        EndPaint (hWnd, &ps);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc (hWnd, nMsg, wParam, lParam);
    }
    return 1;
}

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
    WNDCLASS wndClass = {0, WndProc, 0, 0, hInstance, NULL, NULL, (HBRUSH) (COLOR_WINDOW + 1), NULL, _T("HelloWorld")};
    HWND hWnd; MSG msg; ATOM atom;

    wndClass.hCursor = LoadCursor (NULL, IDC_ARROW);
    if (!(atom = RegisterClass (&wndClass))) return 1;
    if (!(hWnd = CreateWindow (MAKEINTATOM(atom), wndClass.lpszClassName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, HWND_DESKTOP, NULL, hInstance, NULL))) return 1;

    ShowWindow (hWnd, nShowCmd);
    UpdateWindow (hWnd);

    while (GetMessage (&msg, NULL, WM_NULL, WM_NULL)) {
        TranslateMessage (&msg);
        DispatchMessage (&msg);
    }

    UnregisterClass(MAKEINTATOM(atom), hInstance);
    return (int) msg.wParam;
}
  • 이제, Hello World는 찍어봤으니, 이걸 다른걸로 바꿔서 한 번 해보자.
  • 그냥 몇줄 정도 찍어보도록 한다.
#include <stdio.h>
int main(int argc, char* argv[])
{
  for(int loop1 = 0; loop1 < 5; loop1++)
  {
    for(int loop2 = 0; loop2 < 5; loop2++)
      printf("*");
    printf("\n");
  }
  return 0;
}
  • 이렇게, 플로챠트도 필요없을 정도로 단순한 프로그래밍은 이제 다들 할 수 있을거라 생각한다.

연습문제[편집]

  • 네모를 봤으면 세모도 찍어봐야지.
  • 세모 별찍기를 해보자.