2014년 4월 25일 금요일

if / else 문을 한줄로 간단히 표현

if / else 문의 사용에서 각 조건에 대하여 처리하는 연산이 하나(또는 한줄) 인 경우, 이를 간단히 표현할 수 있다.

int v = 30, x;

if (v > 30)
    x = 100;
else
    x = 0;


위의 if / else 문을 한줄로,

x = (v > 30) ? 100 : 0;


코드가 길어지는 것이 싫을 때, 사용하면 좋지 아니할까?

다이얼로그와 관련된 컴파일 에러 대처

아래의 메세지는 해당 다이얼로그를 사용하기 위해
다른 곳에서 #include "DlgView.h"할때 발생됩니다. 

이 메세지는 #include "resource.h"의 위치에 의해 발생하게 됩니다.

 error C2065: 'IDD_DLGVIEW' : undeclared identifier error C2057: expected constant expression 이 에러를 해결하한 방법으로는 2가지가 있습니다.

1. 해당 Dlg 헤더에 #include "resource.h"를 추가해 줍니다.
 2. Dlg 헤더 파일에 자동으로 작성되는 구문인
enum { IDD = DLG_VIEW }; 를 주석처리 하고
 // Dialog Data
 //{{AFX_DATA(CDlgView)
 //enum { IDD = IDD_DLGVIEW };
Dlg 소스 파일을 아래와 같이 바꾸어 줍니다.

//CDlgView::CDlgView(CWnd* pParent /*=NULL*/) 
//: CDialog(CDlgView::IDD, pParent) CDlgView::CDlgView(CWnd* pParent /*=NULL*/) : CDialog(IDD_DLGVIEW, pParent)

[출처] error C2065: ''### : undeclared identifier|작성자 가가고

[C/C++] 폴더 내 파일목록 읽기

특정 폴더 내에 존재하는 파일목록을 얻고자 할 경우 다음과 같이 한다.
먼저 _findfirst 함수를 이용하여 핸들을 얻는다. 이 때, 첫 번째 파라메터에
폴더 및 찾고자하는 파일 명(또는 확장자)을 설정한다. 이 후 _findnext 함수를
이용하여 폴더 내 모든 파일을 검색하며, 결과가 -1이 리턴되면 종료한다.

#include <stdio.h>
#include <io.h>
#include <conio.h>

void main()
{
    _finddata_t fd;
    long handle;
    int result = 1;
    handle = _findfirst(".\\*.*", &fd);  //현재 폴더 내 모든 파일을 찾는다.

    if (handle == -1)
    {
        printf("There were no files.\n");
        return;
    }

    while (result != -1)
    {
        printf("File: %s\n", fd.name);
        result = _findnext(handle, &fd);
    }

    _findclose(handle);
    return;
}

[Windows XP, VS2010에서 동작 확인 함]

2014년 4월 16일 수요일

아스키 코드표



GUI기반 MFC 프로그램에서 Console 창 보이기

MFC GUI 기반 프로젝트에서는 console 창이 나타나지 않는다. 
하지만 디버깅 목적이나 기타 다른 이유로 보고 싶을때,

 #pragma comment(linker, "/entry:WinMainCRTStartup /subsystem:console") 

이걸 추가해주면 콘솔창도 함께 뜬다.


 (From 루오니의 집: http://katalog.egloos.com/4695822)

2012년 3월 21일 수요일

[MFC] memory mapped file I/O

이번 글은 memory mapped file I/O을 통한 대용량 파일의 I/O를 설명하고자 한다. 시작하기 전에, memory mapping의 이점에 대해서는 굳이 설명하지 않더라도, 간단한 구글검색으로 확인할 수 있으리라고 생각한다.

다음의 링크를 통해 memory mapped file I/O에 대한 전반적인 내용을 확인할 수 있다. http://en.wikipedia.org/wiki/Memory-mapped_file

아래의 링크는 MSDN에서 제공되는 memory mapped file I/O를 위한 함수의 설명이다. http://msdn.microsoft.com/en-us/library/aa366537.aspx 

자, 이제 전반적인 내용에 대해 이해했다면, 바로 설명에 들어간다. 우선 나의 경험은 다음과 같다. 대용량(약 7GB이상)의 파일에 접근하여 데이터를 읽어 들인다. 보다 자세하게는 대용량의 YUV시퀀스를 한프레임씩 읽어 화면에 출력한다. 불행하게도, standard I/O 함수 (e.g., fopen, fread, etc.)에서는 (운영체제에 따라 다르지만) 4GB 이상의 크기를 지원하지 않는다. 그렇다면, 대용량 파일을 위해 CreateFile을 이용할 수 있다. 문제는 성능이다. 앞의 링크에서 확인할 수 있듯이, file I/O는 다른 동작에 비해 부하가 크다. 이를 개선하기 위한 방법을 모색하던 중 오늘 다루고자 하는 memory mapped file I/O를 사용하기로 하였다.

windows에서는 memory mapped file I/O를 위한 함수로 크게 세 가지 함수를 제공한다.
1. CreateFile
2. CreateFileMapping
3. MapViewOfFile

각각의 사용법은 위의 MSDN 링크를 확인하면 되겠다. 여기까지 확인한 이후, 아~ 이제 되었다! 라고 안도하던 중, 또 다른 문제를 발견했다. CreateFile, CreateFileMapping이후, MapViewOfFile을 실행하면, 정상적인 포인터를 리턴받지 못하는 상황이 발생하였다.

또 다시 문제를 해결하고자 구글링을 하던 중, 해결의 단초가 될만한 자료를 찾았다. (Thanks to Google) 역시 운영체제에 따라 다르지만, memory mapping을 수행할 수 있는 최대 크기에 한계가 있다는 점이었다. 나는 직접 실험해보지 않았지만, 500MB까지는 한번에 수행가능하지만, 그 이상은 동작하지 않는다는 결과가 있었다. 어차피 나는 500MB를 훨씬 웃도는 파일의 I/O가 목적이었으므로, 문제를 해결해야 했다.

실마리는 I/O를 위한 파일의 일부씩 memory mapping을 수행하는 것이다.
 MapViewOfFile의 파라메터를 확인해 보면, dwFileOffsetHigh, dwFileOffsetLow 그리고 dwNumberOfBytesToMap가 존재한다. 이 파라메터를 적절하게 사용함으로써 성공적으로 memory mapped file I/O를 수행할 수 있었다. 이 때 offset 부분에서 주의해야할 점은, offset은 임의의 숫자를 넣을 경우, 에러가 발생한다(참으로 까다로운 녀석이다). offset은 오직 각자의 시스템에서 지원하는 단위의 배수로만 가능하다. 이 단위는 GetSystemInfo() API를 통해 확인 할 수 있는데, 여기에 파라메터로 들어가는 SYSTEM_INFO 스트럭처의 dwAllocationGranularity를 통해 확인 할 수 있다.

 자! 이제 모든 준비가 끝났다! 이제 문제를 해결해 보자. 앞서 설명한 것과 같이, 내가 구현하고자 하는 것은 무지막지하게 큰 영상을 한장씩 읽어들이는 것이다. 한 프레임은 가로 3840, 세로 2160이며, YV12(4:2:0 YUV)포멧의 영상이다. 그러므로 한 프레임의 크기는 3840 * 2160 * 3 / 2 = 12,467,520 Byte 이다. (크기 계산 방법에 의문이 드시는 분은 역시 Google을 검색해 보시길...^^;;) 그리고, dwAllocationGranularity는 65536 이었다. 따라서 MapViewOfFile을 다음과 같이 호출하였다.

m_hYUVFile = CreateFile(m_cFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if(m_hYUVFile == NULL)
{
 printf("CreateFile() file. Err=%d\n", GetLastError());
 return -1;
}
DWORD dwFileSize = ::GetFileSize(m_hYUVFile, NULL);

m_hYUVMapFile = CreateFileMapping(m_hYUVFile, NULL, PAGE_READONLY, 0, dwFileSize, NULL);
if(m_hYUVMapFile == NULL)
{
 printf("CreateFileMapping() fail. Err=%d\n", GetLastError());
 return NULL;
}

DWORD m_dwMapUnit = systeminfo.dwAllocationGranularity  //65536
DWORD m_dwFrameSize = dwWidth * dwHeight * 3 / 2;  //한 프레임의 크기
DWORD dwMultiple = (DWORD)floor(((double)m_nPresentationCnt*(double)m_dwFrameSize)/(double)m_dwMapUnit);
//m_nPresentationCnt는 몇 번째 프레임인지를 지시한다(순차 증가).
//그러므로 m_nPresentationCnt*m_dwFrameSize는 실제 파일 내에서의 offset을 지시한다.
//앞서 말한대로, 지원하는 단위(m_dwMapUnit)로만 offset을 줄 수 있으므로, m_dwMapUnit에 대한 배수를 계산한다.
DWORD dwDiff = (m_dwFrameSize*m_nPresentationCnt) - (m_dwMapUnit*dwMultiple);
//dwDiff는 실제 파일의 offset과의 차이를 계산한다.

m_pcYUVFile = (char *)MapViewOfFile(m_hYUVMapFile,  //CreateFileMapping을 통해 생성한 핸들러 
                                    FILE_MAP_READ, 0,
                                    (dwMultiple*m_dwMapUnit),  //m_dwMapUnit의 배수로 offset 지정
                                    (m_dwFrameSize+dwDiff));  //파일에서 접근하고자 하는 크기

//m_pcYUVFile에 대한 처리 동작 수행

UnmapViewOfFile(m_pcYUVFile);  //Unmaps a mapped view of a file from the calling process's address space
CloseHandle(m_hYUVMapFile);  //Close file map handle
CloseHandle(m_hYUVFile);  //Close file handle


후아~ 이로써 성공적으로 문제를 해결 하였다. 주의할 점은 full source가 아니므로, 상황에 맞게 수정한 뒤, 사용해야 한다. standard file I/O와 성능 비교는 수행하지 않았으므로, 혹시 성능을 비교해본 분이 있다면, 댓글로 남겨주신다면 매우 감사...요즘 대세인 SSD일 때는 또 어떨까낭...흠냐...

2012년 3월 20일 화요일

Multi-thread 환경에서의 메모리 공유

스레드를 사용하는 프로그램을 작성하던 초기에는, 하나의 스레드 이상을
사용할 일이 없었으므로, 문제가 없었다 (문제가 있더라도 어렵지 않았다).

최근들어 멀티 스레드로 프로그램을 작성하면서 생각하지 못했던 문제와
마주하게 되었다. 바로 스레드 동기화에 관련된 부분이다.

음?! 수업시간에 들었던 기억은 나는데...실제 개발시에 사용해본 일이
없다보니, 기억도 가물가물하고...언제 써야할지 떠오르지도 않고...아래의
코드는 뮤텍스를 통한 두 개의 스레드간 동기화에 대한 예제이다.

#include <stdio.h>
#include <process.h>
#include <windows.h>
//#include <pthread.h>  //유닉스/리눅스 계열에서 뮤텍스를 위한 헤더

#define MUTEX

int cnt = 0;  //각 스레드에서 접근하는 전역변수
#ifdef MUTEX
HANDLE hMutex;  //뮤텍스의 핸들러
//pthread_mutex_t hMutex;  //유닉스/리눅스 계열에서의 뮤텍스 핸들러
#endif

DWORD WINAPI Thread1(void *arg)
{
    int i, tmp;
    for(i=0; i<1000; i++)
    {
#ifdef MUTEX
        WaitForSingleObject(hMutex, INFINITE);  //뮤텍스에 대한 Lock
        //pthread_mutex_lock(hMutex)  //유닉스/리눅스 계열에서의 Lock
#endif
        tmp = cnt;
        Sleep(1000);
        cnt = tmp + 1;
        printf("[1]: %d\n", cnt);
#ifdef MUTEX
        ReleaseMutex(hMutex);  //뮤텍스에 대한 Unlock
        //pthread_mutex_unlock(hMutex)  //유닉스/리눅스 계열에서의 Unlock
#endif
    }
    printf("Thread1 End\n");
    return 0;
}

DWORD WINAPI Thread2(void *arg)
{
    int i, tmp;
    for(i=0; i<1000; i++)
    {
#ifdef MUTEX
        WaitForSingleObject(hMutex, INFINITE);  //뮤텍스에 대한 Lock
        //pthread_mutex_lock(hMutex)  //유닉스/리눅스 계열에서의 Lock
#endif
        tmp = cnt;
        Sleep(1000);
        cnt = tmp + 1;
        printf("[2]: %d\n", cnt);
#ifdef MUTEX
        ReleaseMutex(hMutex);  //뮤텍스에 대한 Unlock
        //pthread_mutex_unlock(hMutex)  //유닉스/리눅스 계열에서의 Unlock
#endif
    }
    printf("Thread2 End\n");
    return 0;
}

int main(int argc, char *argv[])
{
    HANDLE hThread[2];

#ifdef MUTEX
    hMutex = CreateMutex(NULL, FALSE, NULL);  //뮤텍스 생성 및 초기화
    //pthread_mutex_t hMutex = PTHREAD_MUTEX_INITIALIZER;  //유닉스/리눅스 계열에서의 뮤텍스 생성 및 초기화
    //pthread_mutex_t hMutex = pthread_mutex_init();  //유닉스/리눅스 계열에서의 뮤텍스 초기화(동적 초기화)
#endif

    hThread[0]=CreateThread(NULL, 0, Thread1, NULL, 0, NULL);
    hThread[1]=CreateThread(NULL, 0, Thread2, NULL, 0, NULL);

    WaitForMultipleObjects(2, hThread, TRUE, INFINITE);  //두 개의 스레드가 종료될때까지 대기

    printf("%d\n", cnt);  //결과값 출력

    CloseHandle(hThread[0]);
    CloseHandle(hThread[1]);
#ifdef MUTEX
    CloseHandle(hMutex);  //뮤텍스 핸들러 파괴
    //pthread_mutex_destroy(hMutex);  //유닉스/리눅스 계열에서의 뮤텍스 핸들러 파괴
#endif

    return 0;
}

코드를 실행해보면, #define MUTEX를 활성화 시킨 경우, 2000이 출력되며, 비활성화 시킨 경우, 1000이 출력됨을 확인할 수 있다. 비활성화 시킨 경우에는 두 개의 스레드가 하나의 변수(cnt)에 접근하여 1씩 증가 시키는 동작을 수행하는 도중, 스레드1 에서 2로(또는 반대)의 context switching이 발생됨으로 인해 원래의 값을 잃어버리는(또는 변형되는) 상황이 발생하기 때문이다. 반대로 뮤텍스를 사용하는 경우에는 하나의 스레드가 작업을 수행하고 마칠 때까지 (wait(lock)에서 release(unlock)까지) 다른 스레드는 대기(wait(release))하기 때문에 원하는 대로 2000의 결과를 확인할 수 있다.