Thursday, December 23, 2021

Matlab 실행 시 처음 출력되는 작업 폴더 변경하기

  Matlab 실행 시 좌측에 폴더 트리가 뜨게 되는데...

보통은 내가 작업하던 폴더가 아닌 Matlab이 설치된 기본 폴더가 뜨기 때문에 항상 작업하던 폴더로 옮겨가는 절차가 필요해서 불편했다.


이를 해결하기 위해서는 Matlab이 설치된 위치의 bin폴더 안에 

"startup.m" 스크립트 파일 하나만 생성해주면 간단하게 해결이 된다.


이 startup.m은 Matlab 실행 시 자동으로 초기화 절차를 수행해주는 스크립트이다.

기동 시 필요한 작업은 여기에 전부 포함시켜 두면 편하다.


우선 만들어져서 사용되고 있는 startup.m이 존재하는지 확인

>> which startup

(있을 경우 이미 해당 파일이 존재하기 때문에 그냥 수정해서 사용하면 된다. 없으면 생성)


>> edit('Matlab설치된 폴더명\bin\startup.m')

(해당 파일 생성 또는 수정)


만약 G:\develop\Matlab 폴더를 시작시 기본 폴더로 지정하고 싶다면,


addpath(genpath('G:\develop\Matlab'))  %  하위 폴더 전체를 경로에 추가함

cd('G:\develop\Matlab')    % 시작 기본 폴더로 이동


두 줄만 추가 후 파일 저장하면 다음번 Matlab 시작 시 자동으로 해당 폴더에서 시작된다.

Tuesday, December 7, 2021

Anaconda를 이용한 파이썬 개발환경 세팅하기

쓸 때만 기억나지 시간 지나면 금방 잊어 버리는 아나콘다 개발환경 세팅하기


1) 가상 환경 생성하기

$ conda create -n [env_name] python=3.7.11 anaconda

(마지막에 anaconda를 붙여주면 아나콘다 기본툴까지 함께 설치됨)


2) 가상 환경 활성화하기

$ conda activate [env_name]

(비활성화 시 deactivate 옵션 사용)


3) 가장 자주 사용하는 파이썬 패키지들 설치하기

$ conda install numpy, scipy, matplotlib, opencv

$ conda install -c conda-forge pyinstaller=4.7

$ conda install -c conda-forge python-pptx

$ conda install -c anaconda openpyxl

$ conda install -c davidbroadhurst csaps

$ conda install -c intel scikit-learn


4) 가상환경 삭제 하기

$ conda remove --name [env_name] -all


Tuesday, September 7, 2021

Ubuntu 멀티부팅 GRUB 기본값 변경하기

 우분투와 윈도우10을 동시에 사용하다 보면 멀티부팅을 위해 GRUB을 사용하기 마련인데... 경우에 따라서는 이에 대한 설정을 변경해야 될 필요가 생기게 된다.


1. 부팅 시 Grub 화면에서 OS 순서 기억하기... [0번 인덱스부터 시작함]

   내 피씨의 경우에는, 

   0번 Ubuntu, 1번 Advanced Options for Ubuntu, 2번이 윈도우 boot manager


2. 우분투로 로그인 후 터미널 창에서 Grub 설정 파일 로드

   $ sudo gedit /etc/default/grub


   Grub 설정 파일 내용 중 아래의 내용을 수정함

   GRUB_DEFAULT=0 항목을 원하는 인덱스로 변경, 0은 우분투, 2는 윈도우 초기 선택

   GRUB_TIMEOUT=10 항목을 원하는 숫자로 입력, 초기 선택 대기 시간을 선택 가능함


3. 수정 완료 후 수정사항을 반영

   $ sudo update-grub


이후 재부팅하면 수정된 내용이 반영되어 나타남.


끝.

Thursday, March 11, 2021

Tensorflow 텐서플로우 모델 출력에서 Median 값 추출하기

 자주는 아니지만 어쩌다 한번씩 이미지의 출력 통계치를 활용해서 픽셀 값들을 조정해주고 싶을 때가 있다. 평균이야 그냥 reduce_mean 사용하면 쉽게 구할 수 있어서 기억할 것도 없는데... median 값은 그 함수가 저기 안쪽에 처 박혀 있어서 쓸 때마다 찾아봐야 기억이 난다 -_-;;


아래는 그 예시이다.

tf.contrib.distributions.percentile(y_real_pixel, 50.)


첫번째 인수는 이미지를 저장하고 있는 텐서의 이름이고, 두번째 인수에는 그 위치 값을 지정해서 구할 수 있는데... median은 중앙값이므로 그냥 50.0 지정 해주면 된다.

Tuesday, March 9, 2021

Tensorflow 텐서플로우 네트워크 중간에서 값 추출하기 or 타일 생성하기

 텐서플로우 네트워크를 디자인 하다보면,

특정 계수를 뽑아 내거나 혹은 타일 형태로 생성하여 다른 입력과 곱해야 할 경우가 생긴다.

이 때, 두 텐서 사이에 크기가 맞지 않으면 연산이 되질 않아 여간 귀찮은게 아님 -_-


아무튼 이럴 때 tf.split과 tf.tile 조합을 통해 원하는 출력을 만들어 낼 수 있다.


예시는 아래를 참조


gain, offset = tf.split(h_linear_4, num_or_size_splits=2, axis=1)

gain_tile = tf.tile(gain, [1, 128 * 128])
gain_tile = tf.reshape(gain_tile, [-1, 128, 128])
gain_tile = tf.expand_dims(gain_tile, axis=3)

offset_tile = tf.tile(offset, [1, 128 * 128])
offset_tile = tf.reshape(offset_tile, [-1, 128, 128])
offset_tile = tf.expand_dims(offset_tile, axis=3)

output = data_pan * gain_tile + offset_tile

Tuesday, January 26, 2021

우분투 Proxy 네트워크 환경에서의 apt-get 환경 설정 방법

회사 네트워크에 물려서 사용하다 보니 우분투 세팅에, 특히 네트워크 관련 모든 설정들이 엉망이다.
apt-get은 특히나 많이 사용되는 명령어인데, proxy 환경에서 사용하기 위해서는 설정 파일을 별도로 생성하여 설정해주어야 한다.

우선, /etc/apt/apt.conf 파일을 nano에디터를 사용해서 신규로 생성해준다.
(기존에 설정 파일이 있는 경우에는 그냥 그 파일을 오픈)

>> sudo nano ./apt.conf


Acquire::http::Proxy "http://Username:Password@proxy.foo.bar.edu.au:8080";
Acquire::ftp::Proxy "ftp://Username:Password@proxy.foo.bar.edu.au:8080";
Acquire::https::Proxy "https://Username:Password@proxy.foo.bar.edu.au:8080";

위의 내용을 작성 후 저장하면 완료!!!

마지막 부분에 세미콜론 잊지말것.

우분투에서 Proxy 설정하기

 proxy 서버의 설정.. (사내의 경우 proxy 서버를 사용하기 때문에.. proxy서버 설정을 한다.)
1 Firefox 에서 proxy 서버의 설정
Firefox 실행 -> Edit -> Preference -> Advanced -> Settings -> Automatic proxy configuration URL
http://168.219.61.251:8088/samsungcs.pac 이라고 입력후 저장.

2 /root/.bashrc 파일 수정
/root.bashrc 파일에.. 
export http_proxy="http://168.219.61.250:8080"
export ftp_proxy="http://168.219.61.250:8080" 으로 입력 후 저장.. (적용시키려면.. source ./.bashrc 를 입력)

3. 시냅틱 꾸러미관리자에서 http, ftp proxy정보를 수정
    
4 (이건안해도 되는데..) /etc/apt/apt.conf 파일의 수정
만약 file이 없으면 새로 생성한다. 파일 생성후 아래와 같이 입력후 저장..
Accuire::http::Proxy "http://168.219.61.251:8080" 입력후 저장 또는
Accuire {
Retires "0";
HTTP {
};
}; 입력후 저장..




흑체복사와 양자론

흑체 복사와 양자론


막스 플랑크(Max Plank, 1858-1947)는 일생 동안 현대물리학에 중요한 수많은 업적을 이룩했다. 그중에서도 1889년 크리스마스 무렵에 발표된 이른바 '양자가설'은 양자역학의 새 장을 여는 획기적인 것으로 그의 명성을 느높이는 가장 중요한 역할을 했다. 양자가설은 한마디로 '빛의 에너지가 연속적이 아니다'는 것인데, 이는 원래 '흑체복사'라고 불렸던 고전적인 현상을
설명하기 위해 제안됐다. 플랑크의 양자가설을 이해하기 위해 우선 흑체복사와 빛의 성질에 관한 것부터 살펴보도록 하자.


(1)빛은 물결치는 파동
파동은 수면에 돌을 던졌을 때 퍼져나가는 물결과 같은 움직이다. 이러한 운동은 파도처럼 생긴 사인 또는 코사인 함수를 이용해서 수식으로 표현할 수 있다. 우리 눈으로 그 파형을 직접 볼 수는 없지만 소리, 빛 등도 마찬가지로 사인 또는 코사인 함수로 표현된다(소리는 음파, 빛은 광파라고 한다).
빛이 파동인지 입자인지는 오랫동안 과학자들 사이에 논쟁의 대상이 돼왔다. 한동안 빛은 입자로 생각돼 오다가, 파동에서만 볼 수 있는 현상들이 관찰되자 파동으로 인정됐다. 후에 아인슈타인이 광량자설을 발표해 빛이 파동과 입자의 이중적인 성질이 있음을 밝혔지만, 플랑크가 빛을 연구하던 1800년대 말에는 파동의 성질을 가진 것으로 생각됐다.
파동에서 물결 모양 한 개의 길이를 파장이라고 하는데, 우리가 볼 수 있는 가시광선에서 빨간색의 파장은 6백50nm(나노미터, 1nm=10^-9m)정도이고 보라색은 4백20nm정도 된다.
파장은 공간상에서 물결 한개의 길이를 의미하고, 이 파장이 1초 동안 만들어지는 개수가 진동수다. 진동수는 파동의 진행 속도를 파장으로 나누면 된다. 파장이 길면 진동수는 작고, 파장이 짧으면 진동수는 크다. 빛의 전파속도는 모든 파장에서 일정하다. 실제로 빛의 전파속도는 초속 30만km인데, 이때 보라색의 진동수는 7.14×1015, 빨간색은 4.61×1015라는 천문학적인 큰 수가 된다.


(2)스펙트럼은 온도에 관계

태양은 연한 노란 색을 띠고 있는데 태양빛을 프리즘에 통과시키면, 여러 가지 색으로 나누어 진다. 이렇게 한가지 색으로 보이는 광원의 빛이 여러 가지 색으로 나누어진 것을 스펙트럼이라고 한다. 태양빛의 스펙트럼을 보면, 빨간색부터 보라색까지 무지개를 이루는 색 띠가 나타난다. 여기에는 우리 눈에는 보이지 않지만 적외선, 자외선 등의 띠도 있다. 이러한 스펙트럼을 연속 스펙트럼이라고 한다. 백열 전구로부터 나오는 빛도 마찬가지로 연속 스펙트럼이다. 그러나 광원의 빛을 성능이 좋은 분광기로 스펙트럼을 만들어 자세히 관찰하면 스펙트럼 띠의 곳곳에 가는 선이 나타나는 것을 볼 수 있다. 광원이 무엇이냐에 따라서 선의 숫자와 배열이 달라진다.
예를 들어 나트륨을 태워서 나오는 빛의 스펙트럼은 몇 개의 노란색 선으로 이루어져 있다. 19세기말까지 과학자들은 여러가지 물질을 태운 스펙트럼을 연구해 물질마다 고유한 스펙트럼선이 나나탄다는 것을 알아냈다.
한편 이 스펙트럼은 온도에 따라서도 달라진다. 철을 뜨겁게 달구는 과정을 예로 들면, 온도가 낮을 때 철은 거의 검은색으로 보인다. 이때의 철은 가시광선 영역의 빛을 거의 내지 않는다는 것을 알 수 있다. 그런데 온도가 올라감에 따라 철은 빨갛게 되다가 점점 노란색에 가까워진다.
이 과정에서 철이 내는 빛의 스펙트럼을 보면, 철이 붉어 보일 때는 스펙트럼의 여러 색 중에서 붉은색이 가장 강하고, 온도가 올라가면서 다른 색의 강도도 점차 강해지는 것을 알 수 있다.
즉 온도에 따라 스펙트럼 띠에서 가장 강한 색이 결정된다. 그러므로 눈에 보이는 색깔을 보면 철이 어느 온도에 이르렀는지를 대강 알 수 있다. 실제로 1800년대에는 철을 제련할 때 달아오른 철의 색깔을 보고 온도를 알아내 공정을 조절했다.


(3) 빛의 파장은 온도에 반비례
빈(Wien, 1864-1928)은 철의 제련 연구를 위해 세워진 독일의 연구소에서 일하고 있었다. 빈은 1893년, 겨우 25세라는 젊은 나이에, 물체가 내는 빛의 스펙트럼에서 가장 강한 빛의 파장은 그 때의 온도에 반비례한다는 법칙을 발표했다.
즉 어떤 온도에서든지 물체는 여러가지 파장의 빛을 내지만, 전체의 색은 가장 강한 빛의 파장으로 결정되며, 가장 강한 빛의 파장은 물체에 상관없이 온도에 의해 결정된다는 주장이다. 온도가 아주 낮을 때는 가장 강한 빛의 파장이 적외선 영역에 있으므로 우리 눈에 보이지 않는다. 온도가 올라감에 따라 강한 빛의 파장이 점차 짧은 쪽으로 옮겨가면서 노란색이 강해지게 된다. 더 온도가 높아지면, 파란색 쪽으로 옮겨가겠지만, 철은 그 전에 녹아버린다.
빈의 이론이 철의 온도에 따라 색이 변화하는 것을 설명하기는 했지만, 정말 그 이론이 확실히 맞는 것으로 인정받으려면, 엄격한 실험으로 증명돼야 했다. 앞서 언급했듯이 스펙트럼은 온도에 영향을 받지만, 다른 한편으로 빛이 나오는 물체에 의해서도 영향을 받는다. 빈의 이론은 온도의 영향만을 지적했으므로, 실제는 정확히 맞아 떨어지지 않았다. 그래서 가정된 것이 광원의 성질에 영향을 받지 않고 온도에만 영향을 받은 이상적인 물체인 흑체였다.


(4)모든 빛을 흡수하는 흑체
모든 물질은 자신이 온도가 높을 때는 빛을 내고 낮을 때는 흡수하는 성질이 있다. 그런데 각 물질은 자신이 온도가 높을 때 방출하는 빛을 온도가 낮을 때 다시 흡수하는 성질이 있다. 예를 들어 나트륨을 태우면 몇 개의 노란 선이 나온다. 그런데 백열전구의 빛을 차가운 나트륨 기체에 통과시키면, 스펙트럼에서 나트륨의 노란 선만 까맣게 빠져있다. 같은 파장의 빛을 나트륨 기체가 흡수해버린 것이다. 이렇게 모든 물질이 자신이 뜨거울 때 내는 빛을 차가울 때 흡수하는 성질에 착안한 과학자들은 만일 차가울 때 모든 파장의 빛을 흡수하는 물체가 있다면, 이 물체는 뜨거워졌을 때 다시 모든 파장의 빛을 골고루 내놓을 것이라고 생각했다.
모든 파장의 빛을 흡수한다는 것은 그 물체가 아주 까맣다는 것을 의미하므로 과학자들은 우선 검은 빛을 띠는 물질로 결과가 정말 그러한지 실험했다. 그러나 검은색의 물질들이라고 해도 결과는 예상과 맞아떨어지지 않았다. 빈은 이때 아주 기발한 착상을 했다. 속이 비어 있는 상자에 아주 작은 구멍을 뚫으면 이 상자에 들어간 빛은 상자 내부에서 이리저리 반사되겠지만, 이 빛이 들어온 구멍을 다시 찾아서 나올 확률은 굉장히 적다. 즉 흡수된 빛이 나오지 못하는 상황을 만들어낼 수 있다.
빈은 실제의 물질 대신 검은 상자(흑체)를 생각해냄으로써 그의 이론을 실험해 볼 수 있게 됐다. 실험 결과 상자의 재료가 무엇이든 상관없이 상자의 온도를 올릴 때, 상자의 구멍에서 나오는 빛의 스펙트럼에서 가장 강한 빛의 파장은 빈의 예측과 정확하게 들어맞았다.


(5) 짧은 파장은 빈, 긴 파장은 레일리
스펙트럼에서 온도에 따라 가장 강한 빛의 파장을 예측할 수 있게 되자 과학자들은 스펙트럼 전체의 모양을 예측할 수 있는 이론을 찾기 시작했다. 빈은 여기서도 뛰어난 공헌을 했다. 스펙트럼 중 파장이 짧은 쪽, 즉 보라색과 파란색을 아주 잘 예측하는 공식을 생각해낸 것이다. 빈은 검은 상자 속을 채우고 있는 빛의 에너지가 상자 속에서 움직이는 기체 분자의 에너지의 모양과 비슷할 것이라는 가정에서 하나의 식을 제안했는데, 이 식은 짧은 파장의 빛에 대해서는 잘 맞았다. 그러나 긴파장의 빛에서는 식이 잘 맞지 않았다.
그러자 이번에는 레일리(Rayleigh, 1842-1919)가 긴 파장 쪽을 설명하는 식을 제안했다. 레일리는 빛이 파동이라는 성질을 이용해 물결 모양 한 개마다 어떤 일정량의 에너지를 갖는다는 가정에서 출발했다.
그런데 이 식은 파장이 긴 붉은색 빛은 잘 설명했지만, 빈이 설명한 가장 강한 빛의 파장을 예측하지 못하고, 짧은 파장의 빛의 분포도 설명하지 못했다. 그리고 빛의 파장이 짧아지면서 상자를 채울 수 있는 물결 모양의 개수가 점점 많아지는데, 그러면 점점 많은 에너지가 필요하게 된다. 그런데 상자 속에 있는 에너지가 그렇게 무한정 많다고 생각하는 것은 무리였다.


(6) 양자가설로 흑체복사 해결
이렇게 흑체복사의 문제가 풀리지 않고 있을 때, 1899년 12월 플랑크는 새로운 제안을 했다. 플랑크는 빛의 에너지가 연속적인 값이 아니라 어떤 단위값의 정수배인 특정한 값만 갖는다는 가정을 세웠다.
즉 각 빛은 진동수에 비례(파장에 반비례)하는 에너지만 주고 받을 수 있다고 가정했다. 플랑크는 이때의 비례상수를 h로 두었는데, 후에 이를 플랑크 상수라고 부르게 됐다. 플랑크의 가정대로 에너지가 h의 정수배로 묶여서 전달된다면 파장이 짧을 수록(진동수가 많을수록) 많은 에너지가 필요하다는 뜻이 되고 이것은 레일리의 식과 비슷하게 된다. 그런데 플랑크는 에너지가 클수록 그러한 빛이 존재할 확률이 적다는 가정을 추가했다.
때문에 레일리의 식에서 도출됐던 파장이 짧아지면 에너지가 무한정 많아지는 모순을 해결할 수 있었다. 이렇게 해서 만들어진 플랑크의 식은 모든 파장의 빛에 대해 잘 맞았고, 드디어 흑체복사의 문제가 풀리게 됐다. 흑체복사에 관한 공로로 빈은 1911년, 플랑크는 1918년에 각각 노벨 물리학상을 받았다.
흑체복사에 관한 공로만 따진다면, 빈의 공로가 플랑크보다 더 크다고 할 수도 있다. 그런데 놀라운 이야기는 그 다음부터 시작됐다. 플랑크가 제안한 "진동수에 비례하는 값의 에너지만 가능하다"는 생각은 플랑크 자신도 '운좋게 선택된 공식'이라고 할만큼 뚜렷한 물리학적인 근거가 없었다. 플랑크 자신도 이미 알려져 있는 물리학에서 그러한 공식을 사용할 수 있는 근거를 찾아내고자 많은 노력을 기울였다.


(7) 에너지는 불연속이다
그런데 이 말을 조금만 더 생각해보자. 진동수를 f 또는 ν('뉴'라고 읽음)로 많이 표현하는데 플랑크의 말에 따르면, 진동수가 ν인 빛의 에너지는 hν, 2hν, 3hν, 4hν, 등의 값만 가질 수 있다는 말이 된다. 이것은 마치 hν만큼 에너지를 가진 덩어리가 한개, 두개, 세개, 네개가 있는 것과 같은 결과가 된다.
이것은 두 가지 의미에서 중요하다. 우선 에너지가 연속적인 값을 갖지 않는다는 것이 당시로서는 납득될 수 없는 일이었다. 둘째로 이 말은 1905년에 발표된 아인슈타인의 광량자설과 잘 들어맞는다.
우선 에너지의 값이 불연속이라는 생각은 많은 학자들을 당황하게 했다. 빛이 파동이라고 볼 때, 그 에너지가 불연속이어야 하는 이유를 찾을 수 없기 때문이었다. 그런데 빛이 작은 에너지 덩어리들의 모임이라고 생각하면 불연속이라는 개념이 이상하지 않다. 이렇게 해서 빛이 입자이기도 하고 파동이기도 하다는 개념이 성립할 수 있었던 것이다.
즉 입자와 파동이라는 단어 자체가 맨눈으로 관찰되는 거시적인 대상들을 다루면서 만들어진 단어인데, 아주 작은 미시적 대상들을 다룰 때는 이러한 구별 자체가 의미가 없어진다.
미시적인 세계에서는 빛이 어떤 현상은 입자의 성질로, 어떤 현상은 파동의 성질로 관측되는 것이다. 이러한 빛의 입자-파동 이중성은 플랑크와 아인슈타인 이후 여러학자들에 의해 확인됐고 양자물리학의 기본 개념으로 자리잡았다.


(8) 양자역학의 꽃이 피다
빛이 입자-파동 양면성을 지니고 있다고 알려지면서 자연계의 대상들을 다시 돌아보게 됐다. 예를 들어 전자는 질량이 작은 음전하를 가진 입자다. 1923년 프랑스의 드 브로이(de Broglie, 1892-1960)는 지금까지 입자로 생각되던 전자가 파동의 성질을 가지고 있다는 이론을 제안했다.
이렇게 입자로 생각되던 물질이 파동(이것을 물질파라고 한다)이라는 주장은 실험결과로 확인됐음에도 불구하고 많은 과학자들을 고민하게 만들었다. 그때까지 생각해오던 고전 물리학에서 쉽게 납득이 가지 않는 가설들에 기초한 것이기 때문이었다.
많은 학자들이 일관성 있게 이러한 현상들을 설명하려고 다양하게 시도했다. 1925년 슈뢰딩거(Schrodinger, 1887-1962, 1933년 노벨상 수상)와 하이젠베르크(Heisenberg, 1901-1976, 1932년 노벨상 수상) 두 사람은 각각 독립적으로 물질파를 설명하는 이론체계를 발표했다.
두 사람이 사용한 수학적 방법은 아주 달랐지만, 설명하는 물리적 현상은 같았다. 원자 또는 그보다 작은 미지의 세계에서 왜 파동-입자의 이중성이 중요하고, 양자화된 불연속적인 에너지를 갖는지가 완전히 설명된 것이다.
이는 불연속적인 값, 혹은 작은 덩어리라는 뜻의 양자(量子)라는 대상을 다룬다고 해서 양자역학이라 부른다. 한편 물질의 파동성을 다룬다는 의미에서 파동역학으로 불리기도 한다. 원자의 내부와 그와 관련된 문제가 풀리면서 물리학은 새로운 문을 열었다. 마치 뉴턴의 만유인력 법칙과 운동 법칙들이 우리 눈에 보이는 거시적 세계의 모든 운동을 일목요연하게 설명하는 것처럼 원자와 그보다 더 작은 입자들의 미시적 세계를 체계적으로 다루는 것이 가능하게 됐다.
양자역학의 탄생은 현대에 실현된 물질에 대한 깊은 이해에 절대

적인 공헌을 했다. 원자력의 이해와 이용이 한 예이며, 각종 가전 제품과 컴퓨터에 중요한 반도체의 이용도 양자역학으로 가능해졌다. 그 자신도 확신하지 못한 채 도입했던 플랑크의 양자가설은 양자역학이라는 새로운 과학세계를 열어준 출발점이었던 것이다.

The Best Way to Learn Python - 파이썬 강좌를 통한 공부 방법

원문 : The Best Way to Learn Python - Tuts+ Code Article(참조를 위해 최소한으로 요약 번역하였으니 원문을 꼭 참조하세요!)
파이썬은 이전보다 더 인기있으며, 백엔드 웹서버에서 프론트엔드 게임 개발까지 어디서나 사용되고 있다. 파이썬은 정말 일반적인 목적의 언어이며 자존심있는 프로그래머의 무기 중 필수 툴이다.

임무 1: 기초에서 시작하라

두 가지 버전의 파이썬이 있다: 파이썬 2.7과 파이썬 3.4. 어느 것을 선택할지는 크게 중요하지 않다. 입문자에게는 특히, 차이점이 미미하다. 알아야 한다면, 파이썬 2는 서드파티 지원이 훨씬, 훨씬 더 많고, 파이썬 3는 언어를 설계하는 것이 개발자들의 주요 초점이다.

Wikibooks’ Non-Programmers Tutorial for Python

위키북스는 새로운 것을 배우는 훌륭한 소스이며, 파이썬도 예외는 아니다. 너무 기술적이지 않고 유용하고 보람있는 무언가를 바로 코딩할 수 있다.

The Official Python Tutorial

python.org의 공식 문서보다 더 나은 정보 소스를 찾을 수 없을 것이다. 그러나, 바로 뛰어들기를 원한다면, 시작하기에 베스트 장소는 아닐 것이다. 위키북스보다 더 기술적이며, 언어를 숙달해갈 때 나중에 도움이 된다.
초보자에게는 파이썬 2와 파이썬 3의 가장 큰 차이점은 파이썬 2는 괄호없이 print를 사용할 수 있고, 파이썬 3는 괄호가 있어야 한다. 그게 다다.

임무 2: 튜토리얼과 스크린 캐스트

TheNewBoston’s Python Programming Tutorials

강사가 재미와 듣기 쉬움을 균형있게 전달한다. 프로그래밍에 지식이 없어도 된다.

Nettuts+’s Python from Scratch

프로그래밍 경험없이도 장고를 이용한 다이내믹 웹사이트를 만들게 해준다.

ShowMeDo’s Python Screencasts

완전초보부터 중급까지의 파이썬 테크닉과 관련된 비디오 카탈로그가 아주 많다.

Build a Python Bot That Can Play Web Games

완전 초보에게 추천하지는 않지만, 언급할만한 가치는 있다. 간단한 게임을 하는 파이썬 봇을 만드는 법을 보여준다. 매일 반복적인 작업에 적용할 수 있다.

임무 3: 무료 e북들!

고퀄의 무료 e북이 넘치고 있고, 아래는 그 중 베스트 목록이다.

Learn Python the Hard Way

이름과 다르게 매우 쉽게 배울 수 있다.

Think Python: How to Think Like a Computer Scientist

부제와 같이 이론적인 면을 강조한다. 완전초보에게는 약간 어렵지만 알고리즘 이론과 고급 개념에 대해 읽을 수 있다.

Invent with Python

배워서 써먹고 싶다면, 게임을 만드는 것이 좋다! 파이썬을 몰랐더라도 이 책으로 게임을 만들 수 있다.

The Django Book

웹 개발을 위해 파이썬을 배우고 싶다면, 장고 프레임워크를 사용할 것이다. 파이썬에 능숙하고 프레임워크에 초보라면 이 책이 장고를 가르쳐 준다.

PythonBooks

난이도와 주제 따른 파이썬 책에 대한 공식 위키. 한글 책도 소개되어 있다.

임무 4: 스택오버플로우와 친해져라

스택오버플로우에 초보자 에러와 문제만 있는 것은 아니다. 예로 Hidden features of Python을 한번 봐라. 정식 튜토리얼에 없는 수많은 팁과 트릭을 볼 수 있으며, 중급에서 고급 파이썬 사용자에 매우 도움되는 것이다.

임무 5: 프로젝트 오일러

Project Euler(오일러로 발음한다)에서 400 문제를 풀 수 있다. 각 문제는 대략 50%의 수학과 50%의 프로그래밍이다.
새로운 문제를 해결하면 포럼 글타래를 볼 수 있다. 이 곳에서 많은 사람들이 서로 자신의 해결책과 아이디어를 토론하고 있다. 글타래의 후반 페이지에 있는 많은 해결책이 파이썬으로 되어 있을 것이다. 이것은 정말 여러분의 프로그래밍 능력을 키우는 열쇠가 될 것이다. 여러분보다 빠른 해결책이 있다면 시간을 내서 분석하라.
파이썬으로 된 프로젝트 오일러 문제에 대한 정말 훌륭한 블로그도 있다. 배우려고 한다면, 문제에 막혀서 다른 프로그래머의 작업을 살짝 엿보는 것을 부끄러워 하지마라.

임무 6: 게임 만들기

게임을 만드는 것보다 더 만족스러운 것이 거의 없다. 가파른 학습 곡선이 될 수 있지만, 할 가치가 있고 매우 보람있다. PyGame은 가장 알려진 파이썬 게임 라이브러리이며, 무료 투토리얼도 많이 발견할 수 있을 것이다. 여기 베스트 PyGame 튜토리얼이 몇 개 있다.

Pygame 공식 문서

오리지널 파이썬 튜토리얼과 같이 PyGame 개발자도 소개 문서가 가지고 있지만 너무 기술적이다. 그러나 개발자 문서는 항상 정보의 베스트 소스가 될 것이다.

Invent With Python (With PyGame)

TheNewBoston’s Computer Game Development Tutorial

위의 가이드와는 다르게 더 요점적이지만 완전한 게임을 만들게 하지는 않는다.

임무 7: 인기 라이브러리와 툴 알기

PyPy

CPU 집중적인 작업을 하려면, 파이썬 자체로는 병목이 생기는 것을 알게되어 아마 PyPy를 필요로 할 것이다. PyPy는 처리속도를 높여줄 수 있는 파이썬의 대체 컴파일러이다.

NumPy + SciPy

보통 이 두개는 나란히 간다(SciPy가 NumPy에 의존한다). 수학적이거나 과학적인 연구를 위한 진지한 계산 처리를 한다면 이 두 개의 라이브러리는 베스트 친구가 될 것이다. NumPy와 SciPy는 파이썬의 수학적인 함수와 능력을 확장해주고 작업들을 엄청나게 가속할 수 있다.

BeautifulSoup

BeautifulSoup은 정말 아름답다. 정보를 얻기위해 HTML 페이지를 긁어야 할 필요가 있다면 가져오는 것이 매우 좌절스럽고 머리를 쥐어뜯는 것이라는 잘 알게 될 것이다. BeautifulSoup이 모든 것을 하고 수명 몇 년을 벌어줄 것이다. 강추한다.

Python Imaging Library

Python Imaging Library (PIL)는 이미지를 다루는 모든 일에 훌륭하다.

Django

웹 개발이 목표라면 장고 프레임워크를 사용하게 될 것이다. 가장 널리 알려진 웹 프레임워크이며 학습 리소스도 가장 많다.

임무 8: 오픈소스 프로젝트에 관여하라

언어를 파악하고나면 다른 사람의 코드를 읽고 이해하는 것이 언제나 중요한 스킬이다 - 말할 것도 없이, 배우는 훌륭한 방법이기도 하다. 오픈소스 프로젝트들이 좋다. 다른 사람이 여러분의 코드를 판단하는 것을 걱정하지마라. 당장 공헌해야할 필요도 없다. 개선할 무언가를 봤다면, 훌륭하다! 개선사항을 제출하라. 오픈소스의 존재 이유이다.

참고 링크

멤버 함수를 쓰레드 함수로 만들기

오늘은 간단한 C++ 프로그래밍 기법에 대해 하나 써볼까 합니다.

간단한 기교를 부려볼 겸 클래스의 멤버함수를 쓰레드 함수로 작성하는 방법을 배워 보도록 하겠습니다.
(간단히 쓰레드 사용법도 배우고 일석이조! 야호! )

아직까진 그런 적은 없지만, 쓰레드를 돌릴 때 간간히 멤버함수를 쓰레드 함수로 제작하고플 때가 있더군요.
혹, 그런 분들을 위해 알려 드리겠습니다.
 
 class TestClass {

     INT B;

     VOID TestFunction( INT A ) { B = A };                       //이 함수를 쓰레드로 돌리고자 합니다.
     VOID Start();                                                          //이 함수에서 TestFunction을 호출하지요.

};

 
 Start() 멤버 함수에서 TestFunction()을 쓰레드로 돌리고자 한다고 해보죠.

 VOID TestClass ::Start() {

       HANDLE hThread = ( HANDLE ) __beginthread( NULL, 0, &TestFunction, NULL, NULL, NULL );
       // ........ 무엇가의 작업을 하고.

       WaitForSingleObject( hThread, INFINITE );
       CloseHandle( hThread );
}

  
물론 안됩니다. 쓰레드로 돌아갈  함수는 정적으로 선언되어야 하며 정적 함수여야 하죠. 따라서,
 
 class TestClass {

     static VOID WINAPI TestFunction( INT A );                    // 이제 원래 의도대로 이 함수를 정적 함수로 제작합니다.
     VOID Start();

};

  역시 물론 안됩니다. 직접 닥쳐보시면 알겠지만, 우리가 돌리고자 했던 TestFunction 함수에서는 B라는 비정적 멤버를 참조하고 있습니다.

결국은 다른 방법을 찾아야 합니다.

 class TestClass {

     VOID TestFunction( INT A );                                             //다시 원래대로 돌리고
     static VOID WINAPI TestFunctionThread( LPVOID );            //대신 쓰레드가 돌아갈 정적 함수를 만듭니다.
     VOID Start();

};

VOID Start() {

       HANDLE hThread = ( HANDLE ) __beginthread( NULL, 0, &TestFunctionThread, this, NULL, NULL );
       // ........ 무엇가의 작업을 하고.

       WaitForSingleObject( hThread, INFINITE );
       CloseHandle( hThread );

}

VOID WINAPI TestFunctionThread( LPVOID p ) {
      (  (TestClass* ) p )->TestFunction( 3 );
}


자, 위 예를 통해 해결했습니다. 포인터. 즉 동적 호출을 통해서 정적 멤버 함수를 호출하였습니다.

TestFunction의 인자 값도 동적으로 주고 싶다면,
 
 struct Arg {
      TestClass* p;
       INT A;
};

VOID Start() {

        Arg arg = { this, 3 };

       HANDLE hThread = ( HANDLE ) __beginthread( NULL, 0, &TestFunctionThread, &arg, NULL, NULL );
       // ........ 무엇가의 작업을 하고.

       WaitForSingleObject( hThread, INFINITE );
       CloseHandle( hThread );

}

VOID WINAPI TestFunctionThread( LPVOID p ) {
      Arg* pArg = ( Arg* ) p;
      pArg->p->TestFunction( p->A );     
}
 



Visual C++ 유용한 팁 정리

<Visual C++의 유용한 단축키>

* Ctrl+F3 : 현재 커서가 있는 위치의 단어가 나오는 다음 위치를 찾아줍니다. Ctrl+Shift+F3을 누르면 이전 걸 찾아주죠.

* Ctrl+H : replace입니다. 특히 일정부분안의 변수 이름을 변경하거나 할 때 범위를 선택한 다음에 Ctrl+H를 누르면 편하죠.

* Ctrl+], Ctrl+E : 괄호 짝을 맞춰줍니다. {위에서 누르면 짝이 맞는 }를 찾아주지요.

* Ctrl+L : 한줄을 잘라냅니다. -> 클립보드로

* Ctrl+Shift+L : 한줄을 지웁니다.

* Ctrl+J, K : #ifdef ... #endif 쌍을 찾는데 사용합니다.

* 그외 Ctrl을 누르고 커서나 Delete, Backspace를 누르면 단어 단위로 동작을 하게 됩니다.

* Alt+F8 : 인덴트 정리. 일정 범위를 선택하고 누르면 라인 앞부분의 공백을 자동으로 조정해줍니다.

* CTRL+SHIFT+T : 커서 위치의 단어와 앞 단어가 서로 교체 됩니다.

* ALT+SHIFT+T : 커서 위치의 한줄과 윗줄이 서로 교환됩니다.

* Shift + F9 : 디버그 모드에서 추가하고픈 변수나 등등 앞에 커서를 위치 시킨후 Shift+F9를

누르면 Watch Window에 자동으로 추가.

* Ctrl + Alt + T , Ctrl + Spacebar : 멤버목록 팝업창이 나타납니다 (참고로 대소문에 관계없습니다.)

* Ctrl + Shift + U : 소문자를 대문자로 바꾸고 싶을때 블럭 지정을 한다음 이 키를 누르면 소문자가 대문자로 둔갑.

* Ctrl + U : 대문자를 소문자로 변경

* Ctrl + Shift + 8 : Tab은 ^모양으로 공백문자는 .로 변경, 다시 누르면 설정이 해제



<ALT+F4로 종료안되게 하려면>

BOOL MyApp::PreTranslateMessage(MSG* pMsg)
{
        if(pMsg->message == WM_SYSKEYDOWN &&
                pMsg->wParam == VK_F4) {
                      return TRUE;
        }
}

이 함수를 오버라이드 하여서 집어넣으면 됩니다.  



<제일 짧은, 파일 크기 알아내는 함수>

일단 제가 본 방법들을 열거해 보겠습니다.

      1] 파일을 열고, 파일 끝으로 위치를 옮겨서 그 위치값을 읽는다.

      2] 파일을 fopen()이 아닌 open()으로 열어서 핸들을 얻은 다음 그것을 이용해서 filelength()함수로 파일 크기를 구한다.



간단하니 결론만 말씀드리죠. 저는 다음과 같이 파일 크기를 구합니다.

      fileSize = filelength( fileno(fp) );

fp는 FILE *fp; 로 정의된 파일 포인터입니다.

물론 fopen()으로 파일을 연 상태에만 가능합니다.

그러나 파일 크기를 구한다는 것이 DIR프로그램이 아닌 이상 파일을 열고나서 구하는 거 아니겠습니까?



<클래스 이름 등록방법>

PreCreateWindow(CREATESTRUCT& cs)에서 cs.lpszClass를 변경해주면 될 것 같은데 잘되지 않는 경우가 있습니다. 이부분에서 사용자들이 헤매는 경우가 많이 있죠.

다음의 코드를 활용하시면 잘되실 겁니다.



BOOL CTestDlg::PreCreateWindow(CREATESTRUCT& cs)
{
     if(!CWnd::PreCreateWindow(cs))
          return FALSE;

     HINSTANCE hInst=AfxGetInstanceHandle();
     WNDCLASS wc;

     ::GetClassInfo(hInst, cs.lpszClass, &wc);
     wc.lpszClassName=cs.lpszClass="MyClass";

     return AfxRegisterClass(&wc);
}



<외국 리소스를 한글 리소스로 수정하기>

1. 간단하게 고치기

영국인가 에서 만든 소스를 분석하다가 리소스를 볼려고 누르니 언어가 다르다고 안나오더군요 계속 고민하다가 이럴 때는 rc화일에서

원래 코드

//*****************************************************
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
#ifdef _WIN32
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
#endif //_WIN32
//*****************************************************



한국말로 수정한뒤 코드

//******************************************************
#if !defined(AFX_RESOURCE_DLL)||defined(AFX_TARG_KOR)
#ifdef _WIN32
LANGUAGE LANG_KOREAN, SUBLANG_DEFAULT
#pragma code_page(949)
#endif//_WIN32
//******************************************************



이렇게 수정해주니 코드가 나오더군요.

물론 다아시겠지만 열기에서 rc화일을 text로 열어서 수정해주셔야겠지요



2. 근본적 해결방법

다 아시겠지만 VC++은 다국어 리소스를 지원합니다.

외국에서 만든 샘플 예제를 Down받아서 리소스를 오픈할때 현재 시스템의 코드 페이지와 다르면 오픈되지를 않습니다.

지원하지 않는 코드 페이지 때문에 리소스가 제대로 오픈되지 않는다면 해당하는 코드 페이지의 .nls(National Language Support)파일을 시스템에 복사해 넣어주면 됩니다.

해당 nls파일은 Win95/98 CD에 있고 이파일(예:cp_1252.nls)을 windows system 폴더안에 복사한 후 레지스트리(\HKEY_LOCAL_MACHINE\System\CurrentControlSet\control\Nls\Codepage)에 수동으로 등록시켜 주면 됩니다.



<F10 키를 처리하기>

도스시절 프로그램에서 F10 키는 주로 풀다운 메뉴를 띄우는데 사용됩니다.

그래서 우리는 F10 으로 메뉴를 뛰우는데 익숙하죠. 윈도에서도 마찬가지로 F10 키가 메뉴를 활성화시키는데 사용되죠. F10 키가 메뉴를 활성화 하는 것을 막고 나의 고유 기능을 실행하게 할려면 어떻게 해야 하죠?

몇가지 방법이 있는데...

1.- SetWindowsHookEx() 의 사용

키보드의 행동을 바꾸기 위해서 SetWindowsHookEx 함수를 사용하는 것입니다.우선 키보드 훅(Hook)을 작성하고 쓰레드 전반에 이 훅을 설치하는 것이죠.코딩이 좀 까다롭지만..오버헤드가 별루 없기 때문에 ..가장 좋은 제안이 될것입니다.

2.- PreProcessMessage() 와 WM_SYSKEYDOWN 테스트

메시지 선처리기인 PreProcessMessage 에서 WM_SYSKEYDOWN 메시지가 발생했을때 F10 키인지 확인하는 것입니다.그러나 매번 조사해야 하기 때문에 (모든 키에 대해서..) 많은 오버헤드를 발생시켜 속도저하를 가져옵니다.

3.- F10 키에 대한 억셀레이터 테이블 작성

추천할만한 다른 좋은 방법입니다. 그러나 잘 안먹힐때가 있어서.

4.- OnSysKeyDown() 핸들러에서 F10 테스트

문서화되어 있는 방법이죠...그런데 ...이해할수 없는건..

MFC 에서는 F10 키에 대한 SysKeyDown 메시지를 SDI View 에 보내지 않습니다. 문제의 소지가 되죠.



<ESC키로부터 Dialog 사라짐을 방지>

ESC키로부터 Dialog 사라짐을 방지하려면



// 멤버 변수
        BOOL m_bClose

// 생성자
        m_bClose = FALSE;

// OnClose
void CKeyDlg::OnClose()
{
        m_bClose = TRUE;
        CDialog::OnClose();
}

// WindowProc
LRESULT CKeyDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
        if( message == WM_COMMAND && wParam == WM_DESTROY )
        {
                if( m_bClose == FALSE )
                        return NULL;
                // esc키로 부터 다이얼로그 사라짐을 방지
                // 요길 FALSE(NULL)로 막아주면
                // (정상적으로는 아래 return 값이 1로 됨)
                // 다음에 message WM_DESTROY, WM_NCDESTROY가
                // 날아오지 않아 종료 되지 않음
        }
        return CDialog::WindowProc(message, wParam, lParam);
}


/// 참고
// dialog에서 esc 또는 close 버튼은  message - 111, wParam - 2 가 날아옴
// return 값이 TRUE 이면
// message - 2에 이어 message -82가 날아옴


//#define WM_COMMAND                      0x0111
//#define WM_DESTROY                      0x0002
//#define WM_NCDESTROY                    0x0082
//#define WM_CLOSE                        0x0010

/* Enter 키 */

버튼 중에 Property에 Default라고 체크가 된 것이 있을 것입니다.

버튼중 하나만이 이 속성을 가질 수 있는데 Enter키를 치면 이 버튼이 실행됩니다.

Default로 IDOK버튼이 먹게 되지요. 해서 다른 버튼에 이속성을 주거나 IDOK버튼의  함수를 고치세요.



<fscanf()에서 쓸데 없는 값 읽지않고 버리기>

화일명: aaa.dat
자료: 1 2.0 3

이럴경우 자료를 fscanf()로 읽어 들일 때 불필요한 자료가 있을 수 있습니다. 만약 위의 데이터를 읽어 들인다면 -> 실제로 2.0은 필요 없는 자료라 가정하고

int a, b;
float dummy;
FILE* fp;

if((fp = fopen("aaa.txt", "r"))== NULL)
   return;

fscanf("%d %f %d", &a, &dummy, &b);
fclose(fp);

일반적으로 필요없는 2.0도 dummy라고 읽어 들인다음 버립니다.

이런 쓸데가 없는 데이터를 읽어 들이면 시간도 낭비고... 에구 코드도 복잡하고....

이럴 경우 *를 사용하시면 아주 좋죠... ^^;

fscanf("%d %*f %d", &a, &b);

요렇게 바꾸면 실수형 자료가 나오면 그냥 넘어 가요...

각각의 데이터 자료형만 맞추어 주면 그냥 안읽고 넘어간 다음에 그 다음 데이터를 읽지요

자신이 만든 데이터라면 이런 경우가 없겠지만 남이 만든 데이터를 읽어 올 경우에

자기에게 불필요한 데이터가 생길 경우 일일이 다 읽어서 버린다면 넘 짜증 나겠지요 ^^;



<Toggle 기능 구현하기>

버튼을 누를 경우 서로 토글되는 기능을 구현 하겠습니다.

static int rwflg = 1;
rwflg = !rwflg;//가장 중요
if(rwflg) {
      // 버튼을 누를 경우 두번째 실행.
} else {
      //버튼을 누를 경우 처음 실행
}



<한글 윈도우에서 일본어 프로그램 빌드하기>

일단. 리소스는 당연히 일본어 코드로 변환되어 있어야 합니다. 한글 윈도우에서 보면 코드가 깨져 보이겠지요? 코드 변환 방법은 Microsoft Word 같은 걸 이용하셔도 되고, 아니면 프로그램을 만드셔도 되구요.. 가능하면 리소스 작업은 일본어 윈도우에서 하시기를 권합니다. Copy&Paste 로 했는데도, 일부코드가 깨어지는 문제가 있더라구요.. 정확히 이유는 모르겠지만, 리소스의 Text 를 붙여넣는 작업은 가능한한 일본어 윈도우에서 하시기를 권합니다. 그리고 빌드하실 프로젝트의 리소스 파일(*.rc 파일) 에는 한국어가 있으면 절대로 안됩니다!! 테스트를 위해서 몇개의 다이얼로그만 일본어로 바꿨는데 빌드가 안되더군요.. 반드시 전체 리소스 파일에는 영어 아니면 일본어 코드만 존재 해야 합니다.

그리고 Resource file (rc 파일들)의 내용을 다음과 같이 바꾸시면 됩니다. 모든 부분을 바꾸어야 하는지는 확실치 않지만, 일단 이렇게 바꾸면 빌드랑 실행에 문제는 없습니다..

1. #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_KOR) 를

    #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_JPN) 으로...

2. LANGUAGE 18, 1 를

    LANGUAGE 17, 1 로...

3. #pragma code_page(949) 를

    #pragma code_page(932) 로...

4. l.kor\\afxres.rc 를

    l.jpn\\afxres.rc 로..

이상입니다.. ^^.. 도움이 됐을까요?? 그리고 주의하실 점은 Font 입니다.. 일본의 폰트에 맞추어 크기랑 이름을 정해줘야만 하지요..



<#과 ##>

'##'에 대안 질문이 나온 김에 '#'과 '##' 대해 이야기를 한번 해볼까 합니다.

먼저 #부터 시작하겠습니다.

#은 C/C++에서 많이 나오는 기호죠. #include 랄지, #define 이랄지. 그런데, #이 다른 용도로 쓰이는 경우가 있습니다.

다음과 같은 코드가 있습니다.

#include <stdio.h>
#define STRING(a) printf(a)

void main(){
    STRING(trowind);
}

매크로를 아시는 분이라면 위에 코드가 어떻게 변환되는지 아실 겁니다. STRING(trowind) 부분이 printf(trowind)로 바뀌겠죠. (이 변환은 preprocessor에서 담당합니다. 자세한 내용은 'Preprocessor' 또는 '전처리기'에대한 자료를 참고하세요.)

이건 명백히 컴파일 에러입니다. trowind라는 변수가 없으니까요.

그럼 이제 STRING() 매크로 함수를 다음과 같이 바꿔보겠습니다.

#define STRING(a) printf(#a)

바꾼거라고는 'a' 앞에 #를 붙인 것 밖에 없습니다. 그러면 위의 STRING(trowind) 는 다음과 같이 바뀝니다.

printf("trowind")

즉 #의 용도는 매크로에서 치환되는 문자열에 큰따옴표를 붙여주는 것입니다. 이걸 잘 이용하면 다음과 같은 기능을 구현할 수 있죠.

#include <stdio.h>
#include <stdlib.h>

// MFC에도 ASSERT란 매크로가 있기 때문에 MY_ASSERT로 합니다.
#defien MY_ASSERT(a) if ( !a ) { printf("Error : "); printf(#a); }

void main() {
    int a = 0;
    int b = 1;
    MY_ASSERT(a > b)
}

(결과는 직접 확인해 보시기 바랍니다.)



##은 #과는 다르게 작동합니다. ##은 주어진 문자열을 그대로 바꿔줍니다. 예를 들어보죠.

다음과 갈은 매크로가 있습니다.

#define RUNTIME_CLASS(class_name) class##class_name;

그럼 다음과 갈이 됩니다.

RUNTIME_CLASS(CMyDocument) -> classCMyDocument

##은 그 사용 예를 찾기가 쉽진 않죠. 하지만, MFC에서는 이 ##을 기가막히게 잘 사용합니다.  MFC로 애플리케이션을 만들 때 기본적으로 만들어지는 클래스 중에 View나 Document 클래스에 보면 DECLARE_DYNCREATE, IMPLEMENT_CYNCREATE 매크로가 있습니다. 이 매크로를 한번 분석해 보세요.



<드라이브 포맷하기>

MSDN에는 설명이 하나도 없네요..



DWORD WINAPI SHFormatDrive(HWND hwnd, UINT drive, UINT fmtID, UINT option)



이런 형식입니다..드라이브는 ID가

0=A, 1=B,2=C....

fmtID는 -1이구여..

option은

0 - 빠른포맷

1 - 전체포맷

2 - 시스템 디스크 작성



<에디트박스에서 커서를 임의의 위치에>

음.. 에디트 박스에서 커서를 뒤쪽이나.. 임의의 위치에 놓을 수 있는 방법을 물어 보시는 분께 도움될 자료입니다.

1. 커서를 맨 끝에

SetSel(-1, -1);

2. 커서를 임의의 위치에

SetSel(n, n);

======================================
1. 응용할 멤버 함수
======================================

에디트 박스의 멤버 함수중에서 커서를 임의의 위치에 놓을 처리를 하는 함수는 SetSel( ... ) 과 ReplaceSel( ... ) 입니다.

두 함수는 많이 사용되기 때문에 아시고 계시리라 믿~~습니다.

  ex) 에디트 박스의 멤버 변수를 다음과 같이 정의된 상태에서 예를 들어 가면서 설명을 드립니다.

      CEdit   변수 m_editMyBox;
      CString 변수 m_strMyBox;

======================================
2. 커서를 맨 뒤쪽에 놓기
======================================

  커서를 맨 뒤쪽에 놓는 방법은 의외로 간단 합니다.

   UpdateData( TRUE );

  //--> 전체를 선택
  m_editMyBox.SetSel( 0, -1 );        

  //--> 현재의 데이터로 다시 치환
  m_editMyBox.ReplaceSel( m_strMyBox );

  //--> 포커스를 에디트 박스로 설정
  m_editMyBox.SetFocus();

이렇게 처리하시면 커서가 데이터의 제일 끝에 놓일 겁니다.

======================================
3. 커서를 임의의 자리에 놓기
======================================

방법은 마찬가지로..SetSel(..) 과 ReplaceSel(...)을 이용하는 것인데요..

어떤 형태로 응용할 수 있냐 하면..

  SetSel( 시작 위치, 끝 위치, ..?? 안살펴 봤음..세번째 인자)에서 보면, 시작 위치를 처음(0)으로 하고 끝 위치를 커서가 놓이고자 하는 위치로 지정한 후에, 현재의 데이터를 처음부터 커서가 놓이고자 하는 위치까지 다른 버퍼에 복사한 후에, ReplaceSel로 복사 합니다.

소스 코드를 보시면 더 쉬우실 겁니다.

UpdateData( TRUE );

//--> 커서가 놓일 위치까지 복사할 임시 버퍼(크기는??)
char cDataBuf[100];

//--> 쓰레기 값을 방지하기 위해.. 초기화
for( int i=0; i<100; i++ )    cDataBuf[i] = NULL;

//--- 6번째에 커서를 위치하고 싶을 경우...
for( i=0; i<6; i++ )
      cDataBuf[i] = m_strMyBox[i];

//****보충 설명.. CString 형의 데이터 같은 경우에서****

데이터를 원할때는 버퍼 형태로 사용을 하셔도 됩니다.

음.. 무슨 말이냐 하면.. 제가 CString 형으로 선언된 멤버 변수 m_strMyBox의 데이터를 임시 버퍼에 복사하는 과정에서 m_strMyBox[i]라고 사용을 했는데..

m_strMyBox는 에디트 박스의 멤버 변수이거든요.

위와 같은 방법을 복사를 하셔도 되고.. 다른 방법도 있겠죠..

//--> 원하는 위치만큼 지정
     m_editTest.SetSel( 0, 6 );

//--> 바꾸면 되겠죠.. 복사된 내용으로..
     m_editTest.ReplaceSel( cDataBuf );

//--> 포커스를 놓으면.. 끝..
     m_editTest.SetFocus();



이렇게 처리하면.. 에디트 박스에서 제일 끝이나 임의의 자리에 커서를 놓을 수 있군요..



<릴리즈 버젼 실행시 런타임 에러 찾아내기>

릴리즈 버젼 실행시 에러가 났을 경우..참 곤란했죠.

물론 고수님들께서야 이미 알고계시겠지만..

나름대로 엄청 가슴이 아팠습니다. 디버그모드는 괜찮은데..릴리즈는 에러를 때리구..어디서 나는지조차 모르겠구...그때 쓸만한 잼있는 방법입니다.(물론 모르는 분에 한해서..^^)



Step 1.

프로젝트 세팅 들어가면..

Link라는 탭 선택해서..보면

Generate Mapfile이라구 있을꺼랍니다.

고걸 선택해서 샥 돌려보면..map파일이 release디렉토리에 생기죠.



Step 2.

그리구 나선..에러를 만들어 돌려보고, 어느 주소에서 에러가 났는지 캡춰

그리구서 map파일에 있는 주소랑 매치해 보는겁니다.

그럼 어느 함수에서 에러가 났는지 알 수 있죠~



<모든 Top Level Windows 에게 메세지 보내기>

SendMessage 아시져? 거기에서 HWND 인자에 HWND_BROADCAST을 넣으면,모든 윈도우에게 메세지가 간답니다..



<일반적인 윈도 소멸 순서>

MFC framework에서, 사용자가 frame 윈도를 닫게되면, 윈도는 기본적으로 OnClose 핸들을 call 한다. OnClose 내부에서는 DestroyWindow를 호출한다. 가장 마지막으로 호출되는 함수는 OnNcDestroy이다. 이 함수는 약간의 정리를 하는 함수로 윈도를 정리하기 위하여 기본적으로 불려지는 함수 이다. OnNcDestroy함수 내부에서는 PostNcDestroy함수를 호출한다.

afx_msg OnClose( )

Framework은 CWnd나 application을 종료 시키기 위해서 이 함수를 마치 시그날처럼 호출한다. 내부의 기본적인 구현은 DestroyWindow를 호출한다.

virtual BOOL DestroyWindow( )

Destroy Window 함수는

  1)  CWnd내부에 있는 윈도를 destroy 한다.

  2)  또한 윈도를 해제하고 입력 포커스를 제거하기 위한 적절한 message를 윈도에게 보낸다.

  3)  윈도의 메뉴 제거  

  4)  application queue를 비운다.

  5)  timer를 제거

  6)  clipboard의 소유주 제거

  7)  Clipboard-viewer chain을 끊는다. ( CWnd가 viewer chain의 맨 위에 있을 경우 )

  8)  WM_DESTROY( OnDestroy )와 WM_NCDESTROY( OnNcDestroy ) 메시지를 윈도에게 보낸다.

하지만 아직 CWnd 객체는 destroy하지 않는다.



이 함수를 오버라이드 하더라도 이 함수를 호출하지 않아도 된다. ( 왜냐면 시스템이 알아서 호출을 해주니까..) 뭐 굳이 하고 싶다면 해도 된다. 만약 내부에 자식 윈도가 있더라도 걱정하지 말자 자식들을 먼저 destroy한 다음에 자신이 죽으니까....



afx_msg void OnDestroy()

Framework이 CWnd에게 현재 CWnd가 소멸되고 있는 중이라고 알려 주기위하여 호출 한다. OnDestroy함수는 CWnd 함수가 화면에서 사라진 다음에 호출된다.

OnDestroy is called first for the CWnd being destroyed, then for the child windows of CWnd as they are destroyed. It can be assumed that all child windows still exist while OnDestroy runs.



afx_msg void OnNcDestroy()

Client가 아닌  영역이 destroy될 때 Frame work에 의하여 불려지는 함수이다. 윈도가 소멸될때 마지막으로 불려지는 함수이다. 기본적으로 약간의 정리를 한후, PostNcDestroy 함수를 호출한다. 대게는 이 함수를 오버라이드 하지 않지만 만약 하게되면 내부에서 상위 클래스의 OnNcDestroy를 호출해야만 한다. 그렇지 않으며녀 내부적으로 윈도를 위해 할당된 메모리가 free되지 못한다.



virtual void PostNcDestroy()

윈도가 소멸된 후 OnNcDestroy함수에 의하여 불려지는 기본함수이다. 사용자들이 상속받아 만든 클래스의 정리를 위한 코드를 삽입하면 된다. ( ex. delete this; )

위의 함수를 그냥 대충 보지 않고 세심히 본 사람이라면 이제 자신이 삽입하기를 원하는 코드를 어느부분에 삽입을 해야되는지 알 수 있을 것이다. 그럼 아래의 예를 통하여 알아보자.

먼저 View class에서는 3가지 소멸 핸들러를 제공한다. 다음을 보자.

BOOL CAniView::DestroyWindow()
{
         // TODO: Add your specialized code here and/or call the base class

         return CView::DestroyWindow();
}

void CAniView::OnDestroy()
{
        CView::OnDestroy();

        // TODO: Add your message handler code here
}

void CAniView::PostNcDestroy()
{
        // TODO: Add your specialized code here and/or call the base class

        CView::PostNcDestroy();
}

위에 나열 된 순서가 바로 view가 닫힐 때 불려지는 순서이다.   우리가 유심히 살펴 보아야할 부분은 TODO의 위치이다. 과연 어떤 이유에서 각각의 TODO의 위치가 결정 되는가?

만약  윈도가 없어지기 전에 작업을 해야할 일이 있다면   4번 중에 코드를 삽입하자. 또한 윈도가 사라진 다음에 작업을 해야 한다면 22번 줄에 삽입을 한다. delete this 함수를 사용한다면 25번째 줄에 삽입하자.

view에서는 제공되지 않지만  OnClose 함수도 TODO의 위치를 잠시 보자.

void CAboutDlg::OnClose()
{
      // TODO: Add your message handler code here and/or call default

      CDialog::OnClose();
}



<프로파일링[profiling]>

끝날때까지 어느 부분에서 어느 정도의 시간이 걸렸는지를 알 수가 있습니다.. 기타.. 다른 기능도 많이 있구요..  

여러기능중에 function timing만 써봤거든요..

output 창에 나온 결과를 간단히 써보면..



program Statistics
------------------
    Command line at 2001 Jan 15
08:51: "D:\myProg\TestApp\PropertyTest\Debug\PropertyTest"
    Total time: 2848.924 millisecond
    Time outside of functions: 11.820 millisecond
    Call depth: 15
    Total functions: 141
    Total hits: 1868
    Function coverage: 51.8%
    Overhead Calculated 6
    Overhead Average 6

Module Statistics for propertytest.exe
--------------------------------------
    Time in module: 2837.104 millisecond
    Percent of time in module: 100.0%
    Functions in module: 141
    Hits in module: 1868
    Module function coverage: 51.8%

        Func          Func+Child           Hit
        Time   %         Time      %      Count  Function
---------------------------------------------------------
    2599.647  91.6     2716.741  95.8       44 CWinThread::PumpMessage(void) (mfc42d.dll)
     130.907   4.6      131.192   4.6      207 CWnd::DefWindowProcA(unsigned int,unsigned int,long) (mfc42d.dll)
      58.981   2.1     2835.973 100.0        1 CDialog::DoModal(void) (mfc42d.dll)



어느 함수가 시간이 젤 많이 걸린지 나오죠.. ^^;;

프로그램짜다가 나중에 한번 돌려보면..

어느 부분에서 퍼포먼스가 떨어지는지 확인할 수 있겠죠..

잡설은 고만하고..



Project 메뉴 Setting에서 Link 탭을 선택하면..

Enable Profiling 에 체크하시고..

Rebuild All 하신 후에..

Build메뉴 젤 아래에 Profile해주시면..

선택창이 뜨는데.. 선택하시고

프로그램 실행한후에.. 종료하면 output창에 결과가 나옵니다..



Windows 2000에서 profile 메뉴가 활성화되지 안된다는 분이 계시는데 해결 방법은 다음과 같습니다.

Visual Studio가 설치될 때 Profile과 관련된 레지스트리 정보를 HKEY_CURRENT_USER 아래에 써넣습니다. 그래서 Visual Studio를 설치할 때 사용했던 계정으로 로그인하지 않으면 profile이 활성화되지 않습니다.

이를 해결하려면 VC를 설치할 때 접속했던 계정으로 로그인을 하던가, 아니면 레지스트리에 값을 써넣어주면 됩니다. text editor를 이용하여 a.reg라는 파일을 만들고 다음의 내용을 넣어줍니다.

a.reg의 내용

REGEDIT4
[HKEY_LOCAL_MACHINE\Software\Microsoft\DevStudio\6.0\General]
"ProfilerInstalled"=dword:1


이 파일을 더블클릭하면 레지스트리에 값이 써넣어지고 VC에 profile 메뉴가 활성화됩니다.

이 정보는 MSDN에서 Q224382로 찾아보시면 됩니다.



<해상도 변경하기>

말 그대로 프로그래밍 상에서 현재의 해상도를 원하는데로 변경할 수 있습니다.

전역함수로 ChangeDisplaySettings 라는 함수가 있더군요.

암데서나 불러 쓸수 있죠.  

이전 해상도를 보관했다가 다시 복귀할 수 있도록 해보겠습니다.



현재의 해상도를 얻어오는 함수는 GetSystemMetrics 입니다.

먼저 현재의 해상도를 저장할 변수를 선언합니다.

class CMainFrame : public CMDIFrameWnd
{
      /// 생략
      // Attributes
 
public:
      int screenY;
      int screenX;
      /// 생략
}



초기화는 요렇게..

// 현재 해상도 보관
screenX = GetSystemMetrics(SM_CXSCREEN) ;
screenY = GetSystemMetrics(SM_CYSCREEN) ;

인자로 받은 해상도로 변경하는 함수를 만듭니다.

void CMainFrame::ChangeDisplay(int Width, int Height)
{
    DEVMODE mode ;
    mode.dmBitsPerPel = 16;   // 16비트 칼라로 변경
    mode.dmPelsWidth  = Width;
    mode.dmPelsHeight = Height;
    mode.dmFields     = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
    ChangeDisplaySettings(&mode, CDS_FULLSCREEN );
    ShowWindow( SW_SHOWMAXIMIZED );
}

전 16비트 칼라로만 변경하도록 했는데, 저것두 인자로 받아서 처리하면 원하는 칼라로 바꿀수 있겠죠??? 그리구 원하는 곳에서 함수를 호출~~~

    // 1024 x 768 로 변경    
    ((CMainFrame*)AfxGetMainWnd())->ChangeDisplay(1024, 768) ;    

프로그램이 종료할때 이전 해상도로 복귀하도록 해야 겠죠? 이건 DestroyWindow() 에서 처리하면 됩니다.

BOOL CMainFrame::DestroyWindow()
{
    if(screenX != GetSystemMetrics(SM_CXSCREEN) ||
        screenY != GetSystemMetrics(SM_CYSCREEN))
        ChangeDisplay(screenX, screenY) ;
    return CFrameWnd::DestroyWindow();
}

이전 해상도를 저장해뒀으니까 그걸로 다시 복귀한거죠.

암튼, 위에서처럼 작업하면 원하는 해상도로 변경시켜서 작업할 수 있죠.



참, 도움말을 뒤져보니깐 윈도우 메시지중에 WM_DISPLAYCHANGE 라는게 있더군요.

근데 이 메시지는 클래스위저드에 들어있질 않네요.

99999999WM_DISPLAYCHANGE 메세지는 직접 호출할 수는 없을 겁니다.

다만 OS가 화면 해상도가 변경되었을 때 모든 Window에게로

보내주는 Message일 겁니다.



해상도 복원시에 변수로 해상도를 가지고 있다가 복원할 필요 없이 그냥

    ChangeDisplaySettings(NULL, 0);

이라고만 하면 화면 복원 됩니다. 이 함수는 해상도 변경만 하지 디스플레이

등록정보 자체를 바꾸진 않기 때문에 위 방식대로 함수를 호출하면 디스플레이

등록정보에 있는 해상도로 그대로 바꿔주기 때문에 더 안정적입니다.



원하는 해상도를 모니터가 받아줄수 있는지 테스트 하시고 바꿔야죠.

그 부분이 빠졌군요.

EnumDisplaySettings를 이용하시면 됩니다.



< 화면 지우기>

1) OnDraw 와 InvalidateRect 이용

VC++ 에는 Basic의 CLS 명령과 같은 간단히 화면을 지우는 명령이 없습니다. 이때문에 다음과 간은 방법을 사용합니다. 예를 들면

CDC::Rectangle(x1,y1,x2,y2);

으로 사각형을 그린후 안을 배경색으로 모두 채우면 지운 것과 같은 효과를 내게 됩니다. windows는 외관상 화면에 문자출력이 되어 있어도 내부적으로는 모두 이미지를 그리기 때문에 이 명령으로 화면을 지울수가 있습니다.

OnDraw 함수와 InvalidateRect 함수를 사용하는 것으로도 간단하게 화면을 지울수 있습니다.

1 OnDraw 함수에 if문을 나누어 놓아서 아무 것도 표시하지 않는 기능을 넣습니다.

2 CWnd::GetclientRect 함수에서 클라이언트 영역 크기를 알아 낸다.

3 그 크기를 파라미터로 하여 InvalidateRect 함수를 실행하면 됩니다.

그럼 좀더 자세히 알아 보기로 하겠습니다.

CRect myRECT;                // x1,y1,x2,y2 정보를 지정하는 오브젝트를 확보
GetClientRect(&myRECT);  // 클라이언트 영역의 x1,y1,x2,y2를 myRECT에 설정.
InvalidateRect(&myRECT); // 이 사각형 영역을 다시 그리기 설정

이 처리는 클라이언트 영역 전체의 크기를 myRECT에 설정하여 그 myRECT에서 나타내는 범위를 다시 그리고 있습니다. 혹시 부분적으로 다시 그리기를 한다면,

myRECT.SelRect(0,0,200,200);//클라이언트 영역의 (0,0,)-(200,200)을 지정
9InvalidateRect(&myRECT);//그 사각형 영역을 다시 그리기 지정.

과 같이 하면 되구요, 더욱이 "클라이언트 영역만"을 지우려고 할때에는 사전에 크기를 알아 놓지 않더라도 다음과 같이 아주 간단하 방법으로 지울수 있습니다.

InvalidateRect(NULL);



2) ExtTextOut 이용

제가 알고 있기론 화면 지우는 방법 중에서 가장 빠른 걸로 알고 있습니다.

처음은 똑같습니다.

CRect mRect;
GetClientRect(&mRect);

이 다음 InvalidateRect가 아닌.. ExtTextOut을 씁니다.

  텍스트의 백그라운드 색깔을 칠하고 싶은 화면색깔로 정하고, 빈문자열을 쓰라는 겁니다. 즉(파라미터로 CDC *pDC를 받는다는 가정하에)

pDC->SetBkColor(필요한 색깔);
pDC->ExtTextOut(mRect.left, mRect.top, ETO_OPAQUE, &mRect, "", 0, NULL);

이렇게 빈문자열을 죽~ 라이팅하면 문자열의 배경색으로만 칠해집니다.

말하자면 화면을 지우는 효과이죠.

디폴트를 사용하시겠다면 SetBkColor는 사용하지 않아도 됩니다.

화면을 단일색으로 지우는데는 그 이상 빠른 방법이 없죠.

MFC의 CDC::FillSolidRect도 내부 소스를 보면 ExtTextOut을 사용합니다.

다만 ExtTextOut으로 사각형을 칠했을 때는 DC의 배경색이 바뀐다는 사실을 잊으면 안됩니다.



3) PatBlt 이용

WM_PAINT에서

CPaintDC dc(this);
CRect rect;
GetClientRect(rect);

dc.PatBlt(0, 0, rect.Width(), rect.Height(), WHITENESS);



- the end of this article -

MFC에서의 멀티쓰레드(Multithread)

MFC에서의 Multithread
 
OS는 구분하지 않지만 MFC는 사용자 편의를 위하여 두 가지 형태로 지원
 
1.     Worker thread
2.     User Interface thread
 
Worker thread
 
::AfxBeginThread() 함수를 이용
 
CWinThread* ::AfxBeginThread(
       AFX_THREADPROC pfnThreadProc,
       LPVOID pParam,
       int nPriority = THREAD_PRIORITY_NORMAL, // 기본적으로 주 Process와 동일
       UINT nStackSize = 0,
       DWORD dwCreateFlags = 0,                      // 0 또는 CREATE_SUSPENDED
       LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL
);
 
우선 Thread를 이용할 함수를 먼저 정의한다
 
UINT ThreadFunc(LPVOID pParam)
{
       int option = (int)pParam;
       …
}
 
만약 인자가 많은 경우에는
 
typedef struct tagPARAMS
{
       ...
} PARAMS;
 
와 같이 한 후에
 
PARAMS *pParams = new PARAMS;
CWinThread *pThread = ::AfxBeginThread(ThreadFunc, &pParams);
 
와 같이 하고
 
UINT ThreadFunc(LPVOID pParam)
{
       PARAMS *pThreadParams = (PARAMS *)pParam;
       ...
       delete pThreadParams;
 
       return 0;
}
 
와 같이 사용하면 된다.
 
Thread를 잠시 중지시키고 싶을 때는 주 Process에서
 
pThread->SuspendThread();
 
다시 돌리고 싶을 때는 주 Process에서
 
pThread->ResumeThread();
 
와 같이 하면 된다.(Thread 자신이 호출할 수는 없다.)
또는 경우에 따라서는
 
Sleep(2000);
 
과 같이 사용할 수도 있는데 이 경우는 제어권을 다른 Process에 넘겨 주게 된다.
 
Sleep(0);
 
와 같이 할 경우에는 우선 순위가 높거나 같은 Process에 넘겨 주고 우선 순위가 높거나
같은 Process가 없을 경우에는 아무 일도 생기지 않는다.
 
Thread를 종료시키고 싶을 때는 TerminateThread() 함수를 사용하면 되는데 이 경우 Thread 함수가
내부 정리를 하지 못할 수가 있기 때문에 다음과 같은 방법이 많이 사용된다.
 
static BOOL bContinue = TRUE;
CWinThread *pThread = ::AfxBeginThread(ThreadFunc, &bContinue);

 
UINT ThreadPrintNum(LPVOID pParam)
{
       BOOL *pbContinue = (BOOL *)pParam;
       while ( *pbContinue )
       {
             …
       }
       return 0;
}
 
와 같이 하고 bContinue 값을 FALSE로 하면 Thread 함수가 종료된다.
 
Thread가 완전히 종료된 것을 확신해야 하는 경우에는
 
if ( ::WaitForSingleObject(pThread->m_hThread, INFINITE) )
{
       // 이곳은쓰레드가확실히종료된상태임
}
 
와 같이 하면 된다. Thread가 죽어 버려서 먹통이 되는 경우까지 대비하려면
 
DWORD result;
result = ::WaitForSingleObject(pThread->m_hThread, 1000);   // 1초기다림
if ( result == WAIT_OBJECT_0 )
{
       // 이곳은쓰레드가확실히종료된상태임
}
else if ( result == WAIT_TIMEOUT )
{
       // 1초가지나도쓰레드가종료되지않은상태
}
 
이 방법을 사용해야 한다. 어떤 Thread가 현재 실행 중인지 알고 싶을 때는
 
if ( ::WaitForSingleObject(pThread->m_hThread, 0 ) == WAIT_TIMEOUT )
{
       // pThread 실행중
}
else
{
       // pThread가실행중이아님
}
 
와 같이 하면 된다.
 
User Interface Thread
 
User interface thread는 그 자체로 윈도우와 메시지 루프를 가지고 있다.
 
class CUIThread : public CWinThread
{
       DECLARE_DYNCREATE(CUIThread)
 
public:
       virtual BOOL InitInstance();
};
 
이 User interface thread는 독자의 윈도우도 가질 수 있다. 일반적으로 전용 Dialog를 띄워
Thread를 처리하는 경우가 많으므로 이 User Dialog를 CMyDialog라고 이름 지었다고 가정하면
 
IMPLEMENT_DYNCREATE(CUIThread, CWinThread)
 
BOOL CUIThread::InitInstance()
{
       m_pMainWnd = new CMyDialog;
       m_pMainWnd->ShowWindow(SW_SHOW);
       m_pMainWnd->UpdateWindow();
       return TRUE;
}
 
와 같이 CMyDialog를 Thread로 띄울 수 있다. 그 다음
 
CWinThread *pThread = ::AfxBeginThread(RUNTIME_CLASS(CUIThread));
 
와 같이 하면 MFC가 알아서 CUIThread를 생성해서 그 포인터를 pThread에 넘겨 준다.
 
아래 예제에는 CMyDialog를 띄우고 주 Process는 사용자의
입력을 기다린다. Dialog의 Design 및 생성은 별도로 이야기하지 않는다. 아래 예제를 사용하기 위해서는
CMyDialog를 만들고 ID를 IDD_MYDIALOG라고 가정하면 CMyDialog의 생성자에 다음과 같이 추가해야 제대로 동작한다.
 
CMyDialog::CMyDialog(CWnd* pParent /*=NULL*/)
       : CDialog(CMyDialog::IDD, pParent)
{
       Create(IDD_MYDIALOG, NULL);
}
 
이제 완전히 별도로 동작하는(Thread로 동작하는) 윈도우를 하나 가지는 것이다. 만약 이것을 Dialog가 아닌
FrameWnd라고 해도 거의 똑같다. 다만 위에서도 언급했듯이 Thread를 이용할 때는 Dialog가 더 일반적일 것이다.
Worker thread에 비해 훨씬 더 많은 기능을 하는 것을 알게 되었을 것이다.
 
나머지 것들은 위의 Worker Thread에 있는 내용과 동일하다.
 
만약 여러 개의 CUIThread 를 여러 개 만들려고 한다면
 
CWinThread *pThread[5];
for ( int i = 0; i < 5; i++ )
       m_pThread[i] = ::AfxBeginThread(RUNTIME_CLASS(CUIThread));
 
와 같이 하면 5개의 Thread가 생성된다.
 
Program Execution Priority(프로그램 실행 우선순위)
 
Thread는 0~31까지의 priority를 가질 수 있다.
 
프로그램의 priority는
 
BOOL SetPriorityClass(HANDLE hProcess, DWORD dwPriorityClass);
 
함수를 이용해서 조정할 수 있다. 첫 번째 인자 hProcess는 ::AfxGetInstanceHandle()로 얻으면 된다.
 
dwPriorityClass
Execution Priority
Description

DLE_PRIORITY_CLASS
CPU가 IDLE일 때만 사용 가능

ORMAL_PRIORITY_CLASS
보통

IGH_PRIORITY_CLASS
높은 우선 순위

EALTIME_PRIORITY_CLASS
최상위의 우선순위


Thread Execution Priority(쓰레드 실행 우선순위)
 
::AfxBeginThread() 함수의 nPriority를 통해서 설정하거나 CWinThread::SetThreadPriority 를 사용해 설정할 수 있다.
 
BOOL SetThreadPriority(HANDLE hThread, int nPriority);
 
nPriority
Execution Priority
Description

HREAD_PRIORITY_IDLE
REALTIME_PRIORITY_CLASS의 경우 16, 그 외에는 1

HREAD_PRIORITY_LOWEST
프로세스의 우선순위보다 2단계 낮은 우선순위를 가진다

HREAD_PRIORITY_BELOW_NORMAL
프로세스의 우선순위보다 1단계 낮은 우선순위를 가진다

HREAD_PRIORITY_NORMAL
프로세스의 우선순위가 같은 우선순위

HREAD_PRIORITY_ABOVE_NORMAL
프로세스의 우선순위보다 1단계 높은 우선순위를 가진다

HREAD_PRIORITY_HIGHEST
프로세스의 우선순위보다 2단계 높은 우선순위를 가진다

HREAD_PRIORITY_CRITICAL
REALTIME_PRIORITY_CLAS의 경우 31 그 외에는 16


프로그래머가 우선순위를 조정해도 Windows Scheduler가 상황에 맞게 조정하기 때문에 우선순위는
생각하고 조금 다를 수 있다.
 
Thread & Memory
 
각 Thread의 지역 변수는 모두 별도로 Stack을 만들고 Local Variable들을 관리하기 때문에 위의
 
CWinThread *pThread[5];
for ( int i = 0; i < 5; i++ )
       m_pThread[i] = ::AfxBeginThread(RUNTIME_CLASS(CUIThread));
 
와 같은 경우에도 각 Thread가 다른 Thread를 침범하는 일은 없다.
 
이 쯤에서 끝났나 싶겠지만… 아직 갈 길이 멀다.
Critical section, Mutex, Semaphore 같은 것들은 다음에…
 
Multithreading synchronization(멀티쓰레드의 동기화) => http://blog.naver.com/xtelite/50023359879
 
프로그램

Worker thread
 
#include "stdafx.h"
#include "console.h"
 
#include <iostream>
 
using namespace std;
 
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
 
// The one and only application object
CWinApp theApp;
 
using namespace std;
 
UINT ThreadPrintNum(LPVOID pParam);
 
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
       int nRetCode = 0;
 
       // initialize MFC and print and error on failure
       if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
       {
             _tprintf(_T("Fatal Error: MFC initialization failed\n"));
             return 1;
       }
 
       static BOOL bContinue = TRUE;
       CWinThread *pThread = ::AfxBeginThread(ThreadPrintNum, &bContinue);
 
       int count = 0;
       while ( count < 1000 )
       {
             count++;
       }
 
       Sleep(1000);
       pThread->SuspendThread();
       cout << "Thread suspended. Waiting for 2 seconds" << endl;
 
       Sleep(2000);
       cout << "Thread resumed" << endl;
       pThread->ResumeThread();
 
       cout << "Quit thread" << endl;
       bContinue = FALSE;
       Sleep(100);
 
       return nRetCode;
}
 
// 쓰레드함수
UINT ThreadPrintNum(LPVOID pParam)
{
       BOOL *pbContinue = (BOOL *)pParam;
       int count = 0;
       while ( *pbContinue )
       {
             count++;
       }
       cout << "Exit thread" << endl;
       return 0;
}
 
User interface thread
 
#include "stdafx.h"
#include "console.h"
 
#include "MyDialog.h"
 
#include <cstdlib>
 
using namespace std;
 
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
 
// The one and only application object
CWinApp theApp;
 
using namespace std;
 
class CUIThread : public CWinThread
{
       DECLARE_DYNCREATE(CUIThread)
 
public:
       virtual BOOL InitInstance();
};
 
IMPLEMENT_DYNCREATE(CUIThread, CWinThread)
 
BOOL CUIThread::InitInstance()
{
       m_pMainWnd = new CMyDialog;
       m_pMainWnd->ShowWindow(SW_SHOW);
       m_pMainWnd->UpdateWindow();
       return TRUE;
}
 
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
       int nRetCode = 0;
 
       // initialize MFC and print and error on failure
       if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
       {
             _tprintf(_T("Fatal Error: MFC initialization failed\n"));
             return 1;
       }
 
       CWinThread *pThread = ::AfxBeginThread(RUNTIME_CLASS(CUIThread));
      
       system("pause");
 
       return nRetCode;
}


출처 : "http://system.tistory.com/entry/멀티-쓰레딩"

전처리문의 종류(#include, #define, #ifdef, ... )

전처리문의 종류(#include, #define, #ifdef, ... )

 

전처리문이란?

실질적인 컴파일 이전에 미리 처리되는 문장을 가리킨다. 선행처리기라고도 한다. 따라서 컴파일러는 사용자가 작성한 코드를 컴파일하기 전에 전처리문에서 정의해 놓은 작업들을 먼저 수행한다.

종류로는 #define, #if, #ifdef, #ifndef, #defined, #undef 등이 있다. 이것은 기존에 있는 방대한 소스 코드를 지우지 않고 활성화 비활성화 하는 데에 가장 많이 이용된다. 즉, 기존에 있는 소스 코드를 건드리지 않는 상태에서 부분적인 컴파일을 하는 것이다.

C의 전처리문이 오는 줄(Line)의 첫 문자는 항상 '#'으로 시작한다. ANSI 표준에 따른 C의 전처리문의 종류가 아래에 나와 있다.

 

- 파일 처리를 위한 전처리문 : #include

- 형태 정의를 위한 전처리문 : #define, #undef

- 조건 처리를 위한 전처리문 : #if, #ifdef, #ifndef, #else, #elif, #endif

- 에러 처리를 위한 전처리문 : #error

- 디버깅을 위한 전처리문 : #line

- 컴파일 옵션 처리를 위한 전처리문 : #pragma 

 

조건 처리를 위한 전처리문은 어떤 조건에 대한 검사를 하고 그 결과를 참(0 이 아닌 값) 또는 거짓(0)으로 돌려준다.

#if : ...이 참이라면

#ifdef : ...이 정의되어 있다면

#else : #if나 #ifdef에 대응된다.

#elif : "else + if"의 의미

#endif : #if, #ifdef, #infdef 이 끝났음을 알린다.

 

이하 각 전처리문의 상세를 설명한다.

 

#include

헤더 파일과 같은 외부 파일을 읽어서 포함시키고자 할 때 사용된다. 이때의 파일은 이진파일(Binary file)이 아닌 C의 소스파일과 같은 형태의 일반 문서파일을 말한다.

 

    #include <stdio.h>        /* 이 위치에 stdio.h라는 파일을 포함시킨다. */

    #include "text.h"           /* 이 위치에 text.h라는 파일을 포함시킨다. */

 

'<...>'를 사용할 때와 '"..."'를 사용할 때의 차이점은 '<...>'는 컴파일러의 표준 포함 파일 디렉토리(또는 사용자가 별도로 지정해 준)에서 파일을 찾는 것을 기본으로 한다.  그리고 "..."'를 사용했을 때는 현재의 디렉토리를 기본으로 파일을 찾게 된다. 아예 디렉토리를 같이 지정할 수도 있다.

 

    #include <C:\MYDIR\MYHEAD.H>

    #include "C:\MYDIR\MYHEAD.H"

 

#define

상수값을 지정하기 위한 예약어로 매크로라고 부른다. 구문의 상수로 치환한다.

또한 #define은 함수 역활과 비슷하게 아래와 같이 쓰일 수 있다.

#define SUM(x) ((x) = (x) + (x))

동작원리는 함수와 같다. 말 그대로 main소스에서 SUM을 호출하면 옆에 있는 더하기 코드가 치환되는 것이다.

 

#define으로 정의할 수 있는 것은 숫자만이 아니다.

#define MYNAME "Young Hee"

printf(MYNAME);

라고 한다면 이는 전처리기에 의해

printf("Young Hee");와 같이 전개되는 것이다.

 

이렇게 #define으로 정의된 것은 일반적인 변수와는 다르다. 그 차이는 명백한데, 예를 들어서

#define MYNAME "Turbo"

char my_name[] = "Turbo"

의 두가지 경우가 있다고 하자. "MYNAME"은 전처리문으로 "my_name"은 문자형 배열 변수로 정의되었다.

printf(MYNAME);

printf(MYNAME);

printf(my_name);

printf(my_name);

이것을 전처리한 상태는 다음과 같이 될 것이다.

printf("Turbo");

printf("Turbo");

printf(my_name);

printf(my_name);

이런 결과에서 우리가 유추해 볼 수 있는 것은 전처리 명령을 사용했을 경우 "Turbo"라는 동일한 동작에 대해서 두개의 똑같은 문자열이 사용됐고, 변수를 사용했을 경우에는 하나의 문자열을 가지고 두번을 사용하고 있다는 것이다. 결과적으로 이런 경우에는 전처리문을 사용했을 경우 메모리 낭비를 가져 온다는 것을 알 수 있다.

 

#undef

#define으로 이미 정의된 매크로를 무효화한다.

#define ADD(a, b) (a + b)

#undef ADD(a, b)

라고 하면 앞으로 사용되는 ADD(...)는 'undefined symbol'이 되어 에러 처리 된다.

 

#if ~ #endif

#if 구문은 if랑 아주 비슷하다. 이것은 어떠한 구문을 컴파일 할지 안할지를 지정할 수 있다.
#define A 1
#if A
    source code.....
#endif
위 source code 부분은 컴파일이 된다. if문에서와 같이 참, 거짓을 구분하여 컴파일이 된다. 위에서 A값은 1 즉 0보다 큰 수이기 때문에 참인 것이다.

직접 아래와 같이 하면 거짓이기 때문에 source code 부분은 컴파일이 되지 않는다.

#if 0

    source code....

#endif

 

#ifdef ~ #endif
컴파일 할 때

#define MYDEF               /* MYDEF는 값은 가지지 않았지만 어쨋든 정의는 되었다 */

#ifdef YOURDEF              /* 만약 YOURDEF가 정의되어 있다면... */

    #define BASE 10         /* BASE == 10 */

#elif MYDEF                 /* 그외에 MYDEF가 정의되었다면... */

    #define BASE 2       /* BASE == 2 */

#endif
와 같이 쓰면 BASE는 상수 2로 치환되어
전처리기가 컴파일러에게 넘겨준다.

#ifndef 헤더명_H__ ~ #endif

헤더파일이 겹치는 것을 막기 위한 일종의 매크로이다. 예를 들어 헤더파일에 어떤 클래스의 인터페이스 선언을 넣었다고 하자. 이 클래스 인터페이스에서 다른 파일의 프로토타입이 필요해서 다른 A 파일을 include 하고 있는데 이 헤더 파일을 include 하는 파일에서 A라는 헤더 파일을 이미 include 하고 있다면 두번 define한 것이 된다. 그러면 SYNTEX 에러가 난다. 그래서 그런 것을 막는 방법의 하나로 #ifndef을 사용한다. 이전에 include되어 있으면 #endif쪽으로 점프해버려 결국 한번 선언되는 것이다.

#include  <stdio.h>    ------ (a)

#include  <stdio.h>    ------ (b)
이러허게 두번 썼다고 하자. 그런데 앞에 이미 include를 했는데 밑에 또 한다면 문제가 된다. 컴파일러가 검사해야할 코드량도 많아진다. 그래서 stdio.h에는

#ifndef STDIO_H__

#define STDIO_H__

가 선언되어 있다. 만약 STDIO_H가 선언되어 있지 않다면 선언한다는 뜻이다. 그 뒤 (b)에서는 이미 (a)쪽에서 STDIO_H__ 을 선언한 상태이기 때문에 전처리기 쪽에서 무시해버린다. 그러므로 컴파일러는 (a)만 검사한다.

 

#defined

define이 여러 개 되어 있는지를 검사할 때 쓴다. 이것은 여러 개를 동시에 검사 할 수 있다.

#if #defined A || #defined B

 

#ifdef와 #if defined의 차이

#ifdef는 정의가 되어 있는지를 테스트 하기 때문에, 한번에 여러 개를 사용할 수 없다. 형식이

#ifdef name

처럼, 하나의 토큰이 되어야 하기 때문이다.

 

여러 개가 정의되어 있는지를 테스트 하기 위해서는

#if defined(MACRO1) || defined(MACRO2)

처럼 사용한다.

#if는 ||로 중첩해서 사용할 수 있다. 형식이, #if expression이므로, C 표현이 올 수 있다.

#if MACRO1 || MACRO2

처럼 사용해도 된다.

 

#error

소스 라인에 직접 에러 메세지를 출력한다. 전처리기가 #error 문을 만나면 그 즉시 컴파일을 중단하고 다음과 같은 에러 메시지를 출력한다.

ERROR : XXXXX.c ########: Error directive: 내용

    - XXXXX.c --> 현재 컴파일 중인 파일명

    - ####### --> 전처리기가 #error 문을 만난 시점에서의 행 번호(헤더 포함)

 

#ifdef __LARGE__

#error This program must be compiled in LARGE memory model!

#endif

이 내용은 만일 프로그램이 LARGE 모델이 아니라면 "#error" 뒤에 표시된 메세지를 출력하고 컴파일을 중지하게 된다.

 

#line

이 명령은 소스 코드의 행번호를 지정하기 위한 것으로 주로 컴파일러에 의해 미리 정의된 __LINE__과 함께 사용된다. C에서는 베이식과 같은 행번호를 사용하지 않는다. 하지만 디버깅을 하다 보면 "행번호를 알 수 있으면 좋을텐데"하는 경우가 있다. 예를 들자면 어떤 표현들이 있고, 그 표현들 중의 어딘가에 잘못된 부분이 있는 것 같은데 정확히 그 표현이 있는 행을 찾지 못한다면 "#line"을 사용함으로써 그 일을 보다 수월하게 할 수 있게 된다.

__LINE__과 __FILE__을 각각 행번호와 파일명으로 변경한다.

#include <stdio.h>

 

#define DEBUG

 

void main(void)

{

    int count = 100;

 

    #line 100               /* 다음 줄번호를 100으로 설정한다 */

                               /* <-- 이 줄의 번호가 100이다 */

    #ifdef DEBUG        /* <-- 이 줄의 번호가 101이다 */

    printf("line:%d, count = %d\n", __LINE__, count);

    #endif

 

    count = count * count - 56;

    #ifdef DEBUG

    printf("line:%d, count = %d\n", __LINE__, count);

    #endif

 

    count = count / 2 + 48;

    #ifdef DEBUG

    printf("line:%d, count = %d\n", __LINE__, count);

    #endif

}

#pragma

컴파일 옵션의 지정. 컴파일러 작성자에 의해서 정의된 다양한 명령을 컴파일러에게 제공하기 위해서 사용되는 지시어이다. 컴파일러의 여러가지 옵션을 명령행 상에서가 아닌 코드상에서 직접 설정한다.  #pragma는 함수의 바로 앞에 오며, 그 함수에만 영향을 준다. Turbo C는 9개의 #pragma문(warn, inline, saveregs, exit, argsused, hdrfile, hdrstop, option, startup)을 지원하고 있다.

 

#pragma inline : 컴파일할 때 어셈블러를 통해서 하도록 지시한다.즉, 인라인 어셈블리 코드가 프로그램에 있음을 알려준다. (명령행 상에서 '-B' 옵션)

#pragma saveregs : 이 홉션은 휴즈 메모리 모델에 대해 컴파일된 함수에게 모든 레지스터를 저장하도록 한다.

#pragma warn : 이 지시어는 Turbo C에게 경고 메시지 옵션을 무시하도록 한다.

 

#pragma warn -par

이는 매개 변수(parAMETER)가 사용되지 않았다고 경고(warnING)를 내지 못하도록 한다. 이와 반대되는 표현은

#pragma warn +par

이다.

경고의 내용 앞에 (+)를 사용하면 경고를 낼 수 있도록 설정하고, (-)를 사용하면 경고를 내지 못하도록 하는 것은 모든 경고에 대해 동일하다. 명령행에서는 "-wxxx"로 경고를 설정하고 "-w-xxx"로 경고를 해제한다. 경고의 종류는 무척 많은데 자주 사용되는 것을 아래에 나타냈다. 모든 것을 알고 싶다면 컴파일러 User's Guide의 명령행 컴파일러 부분을 참고하시기 바란다.

 

par : 전해진 파라미터가 사용되지 않음

rvl : void 형이 아닌 함수에서 리턴값이 없음

aus : 변수에 값을 할당했으나 사용하지 않았음

voi : void 형 함수에서 리턴값이 사용되었음

sig : 부호비트(MSB)가 고려되지 않은 형변환(type-castion)에서 부호 비트를 소실할 수 있음

 

C의 predefined macro

__FILE__ a string that holds the path/name of the compiled file
__LINE__ an integer that holds the number of the current line number
__DATE__ a string that holds the current system date
__TIME__ a string that holds the current system time
__STDC__ defined as the value '1' if the compiler conforms with the ANSI C standard
__cplusplus determines if your compiler is in C or C++ mode. Usually used in headers

 

예)

#include <stdio.h>  

void main(void) 

    printf("The path/name of this file is %s\n", __FILE__); 
    printf("The current line is %d\n", __LINE__); 
    printf("The current system date is %s\n", __DATE__); 
    printf("The current system time is %s\n", __TIME__);  

    #ifdef __STDC__ 
        printf("The compiler conforms with the ANSI C standard\n"); 
    #else 
        printf("The compiler doesn't conform with the ANSI C standard\n"); 
    #endif 
    #ifdef __cplusplus 
        printf("The compiler is working with C++\n"); 
    #else 
        printf("The compiler is working with C\n"); 
    #endif 

 

  프로그래머들 마다 코딩 스타일(암시적 약속)이 있다. 보통 매크로, const 변수는 대문자로 적는 것이 원칙이다. 매크로 함수와 일반함수, 매크로 대상체(object-like macro)와 일반 변수를 구분하기 쉽게 해주는 것이기 때문이다.

#define STDIO_H_

왜 뒤에 _를 붙였을까? 이것도 하나의 암시적 약속이다. 컴파일러 제작회사는 매크로를 정의할 때 사용자들과 이름이 충돌이 나지 않게 하기 위해서 대부분 _를 뒤어 덧붙인다. 또한 _를 하나 혹은 두 개 연속으로 시작하는 것은 컴파일러 내부에서 사용하는 매크로라는 성격이 강하다. 물론 강제적인 뜻은 없으며 단지 관습상 그렇다. 왜 이것이 관습이 되었나 하면 보통 매크로 변수 이름이나 함수 이름을 지을 때 뒤에 _를 붙이지 않기 때문이다. 그래서 함수 제작자들이 _를 단골로 붙였다.