|
'Programming/Code Story'에 해당되는 글 10건
예전에 NSIS를 이용 할 경우 대용량 파일을 배포 할때 오류가 나는 문제에 대해서 포스팅을 한 적이 있다. (자세한 내용은 http://www.yuno.org/295 을 참고)
이 때, 해결 했던 방법 중에서 일반적으로 쓰인 방법이 인스톨러와 데이터 파일을 배포해서 2개 이상의 파일을 이용한 배포였다. 그런데, NSIS 자체에서 압축 기능을 지원하고는 있지만, 별도의 압축 파일을 처리 하는 모듈은 없기 때문에 NSIS의 EXCUTE SCRIPT 를 이용해서 COMMAND로 실행 가능한 7Z 압축 바이너리에 압축 풀 파일을 넘겨주어 압축을 푸는 방식을 이용했다.
즉, INSTALL.EXE 와 DATA.7Z 이렇게 2개의 파일을 배포 하지만 INSTALL.EXE 는 설치 능력은 있지만 DATA.7Z 파일의 압축을 풀 능력이 없기 때문에 INSTALL.EXE 자체에 7Z.EXE 를 함께 넣어서 설치 과정중에 7Z.EXE를 별도의 프로세스로 실행해서 DATA.7Z 를 압축 푸는 방식을 이용했다.
하지만, 이 방식이 조금 문제가 있는 것이.. 표준 스트림 버퍼를 이용을 하고는 있지만 7Z.EXE 에서 출력 되는 메세지가 이유를 알 수 없게 버퍼링이 되어서 현재 상태를 바로 보여 줄 수 없다는 문제가 있다. 다시 말하면, 100개의 파일을 압축 푸는데, 어떤 파일을 압축을 풀었다 라고 100번의 메세지가 DETAIL LOG에 나와야 하지만 어떠한 이유에서 이 출력될 LOG가 버퍼링 되어서 뭉쳐서 나오는 문제가 생긴 것이다.
따라서 설치 시간이 오래 걸리는 대용량 설치의 경우 유저들은 이게 설치가 되는건가? 하는 의구심을 품게 된다. 또한 현재 설치가 어느 정도 진행 됐는지 알 수가 없어서 답답함을 가질 수 밖에 없었다.
우리 회사에서도 NSIS를 이용한 배포를 하는데 항상 저 문제가 마음에 걸렸다. 언젠가 시간이 나면 NSIS용 7Z PLUG-IN을 만들어야겠다! 라고 생각하고 있었는데, 최근에 제작 할 시간이 생겨서 작업을 시작하고 보니.. 이런! 이미 나와있다. -_-
Nsis7z plug-in ( 참고 : http://nsis.sourceforge.net/Nsis7z_plug-in ) 을 이용할 경우 NSIS 인스톨러를 이용해서 압축을 풀 수 있게 된다. 대용량 파일을 압축 풀더라도 버퍼링으로 인한 메세지가 밀려서 나오는 문제도 해결 할 수 있을 뿐만 아니라, 현재 설치 상태를 % 로도 표시를 할 수 있다.
사용법도 매우 간단해서, 기존의 처럼 7Z.EXE 를 더 이상 인스톨러에 포함하지 않아도 된다. 플러그인을 다운 받아서 NSIS7Z.DLL 파일은 NSIS의 PLUGIN 폴더에 넣어주면 준비 끝. 그 다음에는 인스톨 스크립트에 몇줄만 추가 하면 완료다.
이 플러그인을 이용 할 경우 2가지 배포 방법을 이용 할 수 있다.
NSIS는 인스톨러 컴파일을 할때 설치 데이터를 인스톨러에 함께 묶는데, 1) 이때 데이터 파일을 함께 묶는 방법, 그리고 2) 데이터 파일을 인스톨러와 별도로 배포 하는 방법으로 나뉘어 진다
1)의 경우는 작은 용량의 배포에 알맞다.
2)의 경우는 대용량 파일을 배포 할때 알맞다. NSIS가 단일 파일을 2GB 이상 지원 하지 않기 때문에 2GB 이상의 데이터를 배포 한다면 별개의 데이터 파일로 배포 하는 것이 좋다.
또한 비스타에서 실행 파일의 용량이 대용량일 경우 실행 시간이 매우 오래 걸리는 문제가 있는데, 이 문제 역시 별도의 데이터 파일을 이용 할 경우 해결 된다.
자, 그러면 플러그인에서 지원하는 명령어들을 확인해 보자. 이 플러그인은 총 3개의 명령어를 지원한다.
Nsis7z::Extract
제일 기본 적인 압축 해제 명령으로 Nsis7z::Extract "ArchiveName.7z" 와 같은 방식으로 사용을 한다. 압축이 풀리는 동안 인스톨러에 표시 되는 프로그레스바는 정상적으로 그 값을 표시 한다.
참고로, 인스톨러의 실행 위치에 함께 있는 파일을 압축 풀기 위해서는
Nsis7z::Extract "$EXEDIR\ArchiveName.7z" 로 경로를 함께 지정해주어야 한다.
경로를 반환하는 상수들에 대해서는 여기를 참고!
Nsis7z::ExtractWithDetails
기본 압축 해제 명령에 % 표시 기능을 추가 한 명령.
Nsis7z::ExtractWithDetails "DATA.7z" "Installing package %s..." 와 같이 2번째 파라미터에 스트링을 넘겨 주면 그에 알맞게 % 표시를 해준다. 위의 명령을 예로 들면 Installing package %s... 을 Installing package 퍼센트% ( 현재 용량 / 전체 용량 ) 으로 표시해준다.
Nsis7z::ExtractWithCallback
마지막으로 ExtractWithCallback의 경우 압축을 다 푼후 지정한 콜백 함수를 호출한다. 콜백을 호출 해줄 뿐 명령 자체는 Extract와 동일하다.
GetFunctionAddress $R9 CallbackFunction ; CallbackFunction의 호출 주소를 $R9에 저장
Nsis7z::ExtractWithCallback "Data.7z" $R9 ; Data.7z의 압축 해제 후 $R9 주소 함수 호출
위의 3개 함수를 이용하면 각 현재 진행 상태를 안정적으로 표시 할 수 있다. 다만, 이 플러그인의 조금 아쉬운 점은 압축 풀고 있는 파일명을 표시 안해주는 것이다. 하지만, 이 플러그인은 코드가 공개 되어 있으므로 조금만 수정하면 현재 압축 해제 파일명을 DetailPrint 를 이용하여 표시하는 것은 큰 문제가 되지 않을 것이라 생각된다.
--
Trackback Address :: http://www.yuno.org/trackback/426
최근에 VS 2003으로 프로젝트 컴파일을 하면 이상하게 디버거가 죽어버리는 현상을 겪고 있다. 로컬라이제이션을 위해서 SVN에서 Branch를 따서 코드 관리를 하고 있는데 가끔 여러 코드를 동시에 컴파일해서 배포 해야 하는 경우가 생긴다.
그러던 어느날 부터 갑자기(!) 비주얼 스튜디오로 컴파일 하고, 링커가 결과 물을 뱉어 낸 후에 디버거가 달라 붙어서 실행 되는 순간! vs 2003이 뻗어버린다.
-_-
process를 봐도 보통 160mb 정도의 메모리와 함께 실행 되는 녀석이 300kb 정도만 로드 하고는 그대로- 침묵.
한참을 기다려도 소용이 없다. 수차례 시도 하면 결국은 한번쯤 무사히 실행이 되는데 이때 첫 실행때는 vs2003이 좀 바보가 된다. 이때 실행된 프로젝트 파일을 정상 종료하고 다시 실행하면 그 다음부터는 전-혀- 문제 없이 작동하는 이상한 현상이 계속 나타나고 있다. 젠장!
오늘도 10분이상 10번도 넘게 실행했다가 vs 죽이고, 실행 했다가 죽이고를 반복하고 겨우 실행.. 짜증 정말..
그래서 2003을 버리고 안정적일 것만 같은 2005로 가기로 결정! 추가 작업이 필요 할 것 같아서 소스 코드 사본을 하나 더 만들고 사본에서 2005으로 변환 작업 시작.
...
이게 정말 2003에서 컴파일 되던게 맞나 싶을 정도로 쏟아져 나오는 에러들..
무난했던 에러들은 const static var 선언 같은 이전 버전에서는 암묵적으로 int 형으로 받아주는 것들이 이제는 깐깐해져서 나오는 에러인 C4430 에러 백개 정도는 단순히 형 타입을 써주는 것으로 해결.
함수포인터 전달 관련 오류인 C3867 에러 백개 정도는 단순히 & 을 붙여주는 것으로 해결.
그리고 제일 짜증나는 std 오류 백만개.
std에서 stdext로 옮겨가버린 stl의 자제 분들은 변환 작업으로 손쉽게 변환 되었지만 문법 자체가 변해버린 경우는 혹시 잘못 작동 할까봐 쉽게 수정이 조심스러워졌다. 쳇. 그리고는 시간 관계상 포기.
그리고 나서 확인해보니 Visual C++ 2005 컴파일러의 주요 변경 사항을 보니 이제 안되는게 참 많구나.. 라는 생각이 든다.
조만간 날 잡아서 오류들 전부 해결 하고 아무도 모르고 한번 팀에 바이너리 배포 해봐야겠다. 아무도 이상한 점을 발견 하지 못한다면 정상적으로 포팅 한거겠지..
아 이런것 좀 누가 와서 걍 해줬으면 좋겠다.
--
Trackback Address :: http://www.yuno.org/trackback/363
NSIS ( Nullsoft Scriptable Install System )을 사용해서 소프트웨어를 패키징 & 배포를 할때 비스타에서 문제점이 발생한다. 물론 배포 전에 각 OS 별로 테스트를 진행하지만 어떤 조건이 갖추어진 경우에만 티가 나는 경우가 있어서 짜증을 불러 온다. 모든 문제는 Vista의 UAC ( User Account Control )에서 부터 시작 되는 문제이다. Microsft가 Vista를 출시 하면서 기존에는 그다지 신경 쓰지 않던 사용자 계정 간의 보안 문제를 적용하기 시작했다. 즉 계정 별로 시스템에 큰 영향을 줄지 모르는 것들에 대해서 제한적으로 실행을 허가 하는 기능을 추가 한 것이다. 프로그램을 설치 할때 일반적으로 시스템 정보에 접근해서 레지스트리, 유저별, 전체, 단축 아이콘, 바탕화면 아이콘 등을 작업하는 설치 프로그램 역시 UAC의 영향을 받게 되어 버린것이다! 현재 리포트 되어 있는 대표적인 2 가지 문제는 전부 UAC 서비스를 사용하지 않는 상태에서는 문제가 전혀 발생 되지 않는다. 첫번째 문제, Vista에서 대용량 실행 파일을 관리자 권한으로 실행하면 UAC 의 영향을 받아서 소프트웨어 구동 시간이 매우 오래 걸린다. ( 소니 TZ 노트북에서 200MB 짜리 단일 NSIS 설치 파일 실행시 약 20초, 1.5GB 짜리 실행시 약 1분 이상, 거의 10MB당 1초의 구동 대기 시간 소요 ) 결국 이것은 사실상 비스타의 버그!!!!! 이 문제는 예상되는 바로는 UAC가 실행 되는 파일을 전체 검사를 하여 인증서와 같은 것을 검색 하는 것으로 보인다. (관리자로 실행 하기 위해서 manifest 파일 같은 것을 응용 프로그램에 포함시키기도 하기 때문에) 두번째 문제, Vista에서 User 권한으로 프로그램을 설치 하려 하면 관리자 권한 (Administrator )의 폴더에 접근을 할 수 없으며 단축 아이콘 및 레지스트리에 접근이 불가능하다. 아쉽게도 첫번째 문제는 아직 해결 방법이 없다. NSIS 에서 컴파일을 할때 RequestExecutionLever 을 User 로 설정해서 실행 권한을 낮추면 문제 없이 정상적인 속도로 실행이 된다. 하지만 이 경우에는 바로 두번째 문제에 봉착 하게 되어진다. -_- 첫번째 문제를 해결 하는 제일 좋은 방법은 설치 파일을 분할 하는 것이다. 이것은 NSIS에서 분할 파일 배포 방법( http://www.yuno.org/295 ) 을 이용하면 된다. 두번째 문제는 NSIS의 UAC 플러그인을 사용하면 된다. UAC Plugin은 사용자 권한(USER LEVEL)로 실행 되었을 경우 내부에서 해당 실행 파일을 다른 프로세스를 이용하여 관리자 권한으로 다시 한번 실행 시켜주는 방식을 통해서 두번째 문제를 해결 하고 있다. 하지만 첫번째, 두번째 문제가 엮인 경우 두번째 문제를 해결 하기 위해 UAC 플러그인을 사용하면 다시 첫번째 문제로 돌아가게 된다는 것을 명심하자. ( 쪼개는 방법 밖에 없단 이야기 )
또한 UAC 플러그인은 유저단의 아이콘 만들기 라던가, 유저 레벨로의 별도 실행 파일 실행 등의 기본적인 기능을 포함하고 있다. 만약 두번째 문제만 겪고 있을 경우는 UAC Plugin으로 가볍게 문제를 해결 할 수 있다. 이것과 관련된 NSIS Forum의 thread가 있으니 한번 읽어 볼만하다.
--
Trackback Address :: http://www.yuno.org/trackback/326
소프트웨어가 중복 실행 되지 않게 하기 위해서 사용하는방법. 몇가지 방법이 있다.
온라인 게임 클라이언트들이 대부분 이 방법들을 사용하고 있다. 따라서 멀티로더와 같은 것을 만들때 대부분 별도의 디버거를 어플리케이션에 붙여서 관련 함수들을 찾아서 사용한다. ;;
아무튼, single instance를 위한 작업이라면 그냥 아에 알려진 방법을 전부 다 섞어서 사용해도 될 것 같다. -_-.. 그러면 멀티로더를 만드는 사람이 '한가지'를 발견해서 수정해도 작동 되지 않음을 알고 포기 할 수도 있고 (가능성 낮은가-_-).. 최소한 작업시간을 10초라도 더 증가 시킬 수 있으니 ...
사실 대부분의 방법들은 그 목적을 위해서 만들어 졌다기 보다는 다른 용도로 만들어진 것들이지만 그것들을 활용해서 이러한 효과를 볼 수 있는 것이다. 예를 들어 뮤텍스나 세마포어, data_seg 같은 것들은 쓰레드간의 데이터 공유를 위해서 존재 하는 것들인데 이것들을 활용 하는거다. 만세!
뭐 암튼, 뮤텍스와 data_seq의 예제를 대충 남겨둔다. FindWindow야 워낙 알려진 것이니까 스킵.
아.. 그리고 요즘은 별도의 보안 프로그램(HackShield, GameGuard)을 이용하기도 한다.
Mutex
#ifdef _SINGLE_INSTANCE Char sMutexName[ MAX_PATH ]; ::_stprintf( sMutexName, _T( "[ApplicationName].SingleInstance" ) ); this->hSingleInstanceMutex = ::UNICreateMutex( NULL, YES, sMutexName ); if( ::GetLastError() == ERROR_ALREADY_EXISTS ) { if( ::WaitForSingleObject( this->hSingleInstanceMutex, 5000 ) == WAIT_TIMEOUT ) { ::exit( 0 ); } } #endif
#pragma data_seg()
#define _INSTANCE_NAME "[Applicatipn].SingleInstance" #pragma data_seg(_INSTANCE_NAME) DWORD _InstanceCount=0; #pragma data_seg() #pragma comment(linker, "/section:" _INSTANCE_NAME ",rws")
_InstanceCount++; if ( _InstanceCount > 1 ) { ::exit( 0 ); }
아 사실 자세히 전부 다 쓰려고 했는데.. 귀찮다...
--
Trackback Address :: http://www.yuno.org/trackback/315
말랑양이 원해서 한시간에 걸쳐서 만들어 본 것
티스토리와 태터툴스에서 RECENT COMMENT에서 자기 이름으로 달린 COMMENT는 보이지 않게 하는 사이드 바 스킨.
태터툴스는 코드를 고치면 쉽게 되지만 티스토리는 코드 수정이 안되므로 스킨을 수정해서 자바 스크립트를 이용해서 핸들링.
다만, 단점은 출력 갯수가 5개 일 경우는 자기가 5개 달면 자기가 단 5개가 출력 되니까 아무것도 나오지 않게 된다. 그래서 최대 출력 갯수 (티스토리는 30개)를 출력 하게 해 놓고 스킨에서 5개만 출력 하게 강제 수정.
그러면 자기가 최소한 25개의 리플을 최근에 달지 않는한은 5개는 남의 것이 나온다는..;;
자세한건 사용하는 말랑양이 자세히 써주겠지..;
스킨 코드 보기 <div id="rct_comment_div" > <ul> <div id="rct_comment_div_content" > COMMENT </div> </ul> </div> <script> document.all['rct_comment_div_content'].innerHTML=''; var index_count=0; var rct_index=0; </script> <textarea id=rct_rep_message style="display:none"> temporary </textarea> <s_rctrp_rep> <textarea id=rct_rep_message style="display:none">
</textarea> <script> var name=""; rct_index++; if (name !='[자기 ID]' && index_count < 5) { index_count++; document.all['rct_comment_div_content'].innerHTML+='・<a href=><b></b> :: '+rct_rep_message[rct_index].value+'</a><br>'; } </script> </s_rctrp_rep>
--
Trackback Address :: http://www.yuno.org/trackback/297
NSIS(Nullsoft Scriptable Install System)는 어느새인가 국내 배포 프로그램에서 나름 차지 비율을 높여만 갔다. 아직 해외에서 발매되는 주요 상용 소프트웨어는 Install shield를 쓰고 있지만 전 세계의 수 많은 공개 프로그램들은 NSIS를 사용하고 있다.
우리도 꽤 오래전에 Install Shield를 이용해서 배포를 하다가 Install Factory로 바꿨었고 지금은 NSIS를 사용을 하고 있는데 최근에 배포 패키지의 용량이 2GB를 넘어버렸다.
nsis script를 이용해서 컴파일을 하는데 다음과 같은 에러가 나왔다.
Internal compiler error #12345: error mmapping file () is out of range.
Note: you may have one or two (large) stale temporary file(s)
left in your temporary directory (Generally this only happens on Windows 9x)."
뭐 대략 이런 메세지.
처음에는 파일 시스템의 문제인줄 알았다. 그리고 생각하니 NTFS를 쓰는데.. 단일 파일이 2GB를 넘는다 해도 문제가 없을텐데?라는 생각이 들고 하나씩 뜯어 나가니.. 아쉽게도 NSIS는 대용량 배포, 즉 2GB 이상을 공식적으로 지원 하지 않는다.
패키지 파일이 2GB 이상일 경우는 그다지 많지 않다. 결국 NSIS의 패키징 방식을 조금 바꾸어서 분할 배포를 해야 한다. 하지만 아쉽게도 NSIS는 공식적으로 분할 패키지 파일을 지원하지 않는다. 따라서 별도의 편법을 이용해서 배포 패키지를 만드는 수 밖에 없다. ㅠ_ㅠ.
어떤 방법이 있을까 곰곰히 생각해보고, nsis forum에서도 검색을 해보고, google도 좀 해보고..
결국 방법은 2가지로 압축 되었다.
첫번째 방법은 CAB으로 배포 데이터를 분할 압축하고 NSIS는 CAB 데이터를 풀어주고 프로그램 등록 및 레지스트리 등 각종 배포후 작업을 진행 하는 것.
관련한 정보는 http://nsis.sourceforge.net/CABSetup_plug-in 에서 관련 플러그인 자료를 구할 수 있다.
두번째 방법은 7zip (7z) 방식을 이용하는 것이다.
처음에는 더 친숙한 CAB 방식을 선택 했지만 이 녀석과 NSIS를 묶는 것 자체가 더 짜증났다. 한참을 준비하다가 훨씬 더 간단하게 만들 수 있는 7zip 방식으로 급 선회. 이 녀석은 lzma 방식으로 매우 높은 압축 효율율을 자랑한다. ZIP 보다 높은 압축률을 자랑한다. NSIS도 lzma 방식을 사용한다.
http://www.7-zip.org/ 에서 SDK와 인스톨 버젼을 배포 한다. 이 인스톨 버젼에 shell용 압축 해제 프로그램이 포함 되어 있다. 이것을 이용해서 NSIS SCRIPT의 외부 명령 실행 명령을 이용해서 설치 도중에 함께 배포한 데이터 파일을 배포 하는 것이다.
GUI 프로그램까지 함께 배포 하고 있으므로 정말 손 쉽게 일반 압축 프로그램을 사용하는 것 처럼 설치 하고자 하는 풀 인스톨 파일을 분할 압축 또는 단일 압축을 하고 NSIS 스크립트에서는 7zip sdk를 이용해서 만든 별도의 프로그램 또는 함께 배포 해주는 압축 해제 프로그램을 포함해서 단순히 압축을 해주는 것이다.
매우 쉬워서 기존의 스크립트를 새 스크립트로 변경 하는데 10분도 걸리지 않으며 NSIS의 고통스러운 점인 단순히 스크립트의 내용 수정으로 인해서 엄청난 압축 시간을 요하는 매번의 컴파일을 거치지 않아도 된다는 것이다.
자세한 스크립트 설명은.. 나중에 사무실에서 시간이 나면 올려야겠다 ;;
어쨋든, 이리 저리 하여 NSIS 2GB의 저주에서 벗어났다. 만세에~
관련한 7Z PLUGIN 에 대한 설명은 이곳을 눌러주세요.
--
Trackback Address :: http://www.yuno.org/trackback/295
게임 서버와 클라이언트에서 어떤 리스트(명단)을 주고 받을때 자료 구조를 만들어서 쓰기 보다는 주로 STL을 쓴다. 그중에서 많이 쓰이는 것들이 list와 vector인데, 이번에도 역시 서버에서 리스트를 받으면서 vector를 사용하였다.
뭐 워낙 클라이언트에서도 많이 쓰이고 있고, 해당 기능을 구현 하는 곳에서도 많이 쓰였기 때문에 별 생각 없이 코드를 작성, QA 도중에 이상한 문제가 발생 하기 시작했다.
구현된 기능중에 벡터 컨테이너에 저장된 구조체 배열 중에서 특정 값을 가진 아이템들을 전부 삭제 하는 기능이 있었다. 예를 들어서 유저 목록이라고 한다면 A라는 class id를 가진 모든 유저를 삭제 하는것과 같은 기능이다.
처음에 구현된 코드는 다음과 같았다.
iterator iterCur; iterator iterEnd;
iterCur = this->MemberList.begin(); iterEnd = this->MemberList.end();
for ( ; iterCur != iterEnd ; iterCur++ ) { if (iterCur->type == ipPacket->type) { this->MemberList.erase(iterCur); } }
그런데 이 코드를 통해서는 정상적으로 기능이 구현 되지 않았다. 테스트 된 환경중에 2개의 데이터를 벡터에 넣어두고 둘다 지워질 수 밖에 없는 경우의 값을 대입시켰을때 조건이 명확함에도 한개만 삭제 되어졌다. VS의 디버거를 통해 알아본 결과 루프가 1회만 돌고 있기 때문이었다.
1회만 도는 원인이 반복자의 무효화 관계가 있을 거라고 생각하고는 erase 이후에 반복자를 다시 대입시켜 주는 코드로 변경해보았다.
iterator iterCur; iterator iterEnd;
iterCur = this->MemberList.begin(); iterEnd = this->MemberList.end();
for ( ; iterCur != iterEnd ; iterCur++ ) { if (iterCur->type == ipPacket->type) { this->MemberList.erase(iterCur); iterCur = this->MemberList.begin(); iterEnd = this->MemberList.end(); } }
허나 이 역시 매한가지. 그리고는 곰곰히 생각해보았다. 그냥 벡터의 .size()를 통해서 루프를 돌릴까.. 하는 고민을 했으니 배열로 접근 할 경우 삭제가 불가능하기에 접고 저렇게 작동하는 원인을 찾기 위해 노력해 보았다. 그리고는 다음과 같은 결론을 내렸다.
일단 처음의 두 코드의 루프가 전부 돌지 않았던 것은 for 문 자체의 증감식문 때문이었다. 만약 처음의 첫 코드를 이용 할 경우 반복자의 무효화가 발생 할 경우 stl 내부에서 참조 오류를 내며 crash 되어버린다.
그것은 이미 무효화 되어 버린 반복자에 for의 증감식문이 영향을 줘서 그렇다. (처음 두 코드에서 for에 있는 iterCur++ 문) 따라서 루프 속에서 iterCur or iterEnd or iterEnd의 무효화가 일어날 경우를 대비해서 다시 대입 시켜 주어야 한다.
즉, 반복문 안에서 두가지 경우가 일어 날 수 있게 되는 것이다. 현재 반복문의 증감식으로 부여 받은 이터레이터가 유효한 경우는 지워지지 않은 경우이므로 증감식문을 이용하여 증감 시키고, 무효화가 일어나는 경우, 즉 삭제가 될 경우는 별도의 값으로 해당 이터레이터를 대입 시켜 주어 증감식문의 영향을 받지 않게 해야 한다.
하지만 만약 벡터가 삭제가 된 이터레이터 외에 앞 뒤 아이템의 위치 역시 변경 될 경우는 난감할 지도 모르겠다. (이런 경우가 있나?)
어쨋든 대략 이런 구조로 작성 하면 되겠다.
iterBegin = this->MemberList.begin(); iterEnd = this->MemberList.end();
for ( iterCur = iterBegin; iterCur != this->MemberList.end(); iterCur = iterNext ) {
iterNext = iterCur; if (iterCur->type == ipPacket->type) { MemberList.erase(iterCur);
반복문의 증감식을 이용 할 수 없으므로 별도의 값을 대입. 벡터 전체의 재정렬(전체 무효화)가 생기지 않는다면 루프의 증감식문의 영향을 받지 않은 정상 반복자를 별도로 저장 하고 해당 값을 대입해도 된다. 아래와 같이 begin을 대입하면 삭제가 이루어 질때마다 전체를 처음부터 다시 돌것이므로 성능 저하가 온다.
iterNext= this->MemberList.begin(); } else { 반복문의 증감식 대용 ++iterNext; }
어쨋든, for의 증감식문을 전혀 생각 안하고 버릇대로 손이 갔다가 고생좀 했다 ;;
---------------------------------
아래 리플을 보고 알았습니다. -_-. visual assist 쓰면서 몰랐다니. ㅡㅡ; 추가 vector의 erase의 반환값이 있었나 봅니다 -_-. 반환값이 current iterator인가 봅니다~
iterCur = this->MemberList.erase(iterCur);
으로 삭제 된 후의 이터레이터를 받아 올수 있는거 같군요.
--
Trackback Address :: http://www.yuno.org/trackback/273
게임 케릭터 미리 보기 작업을 하느라 처음으로 ASP.NET C#을 이용해서 그래픽 파일을 브라우저로 전송하는 코드를 작성할 일이 생겼다.
ASP.NET을 처음 만져봐서 각종 정보를 찾아 보니 든든한 클래스가 많이 존재했다. Bitmap, ImageConvert, Color 등..
따라서 게임 그래픽 데이터 파일을 로드하여 Bitmap 에 넣고 각종 변환 작업 (색 변경, 조합, 이미지 머지 등) 수행 후에 BMP로 저장 또는 Response.OutputStream으로 바로 이진 전송을 해도 오류가 나는게 아닌가.
오류도 당황스럽게 GDI+ 일반 오류. (A generic error occurred in GDI+) 가 발생 하는게 아닌가..
뭐가 문제란 말이냐!! GIF나 JPG로는 잘 저장이 되는데! 심지어 파일로도 저장이 잘 되는데 BMP나 PNG, TIFF 등 이미지 손실이 없을 것만 같은 것들은 모조리 저장이 안된다니!?
한참을 고민하다가 해결 방법을 찾았다. 바로 목표 스트림으로 출력하는게 아니고 별도의 메모리 스트림을 거쳐서 찍으니까 모든게 해결. ... 쳇;
뭔가 별도의 포멧으로 저장 할때는 스트림에서 읽었다 썼다를 반복이라도 하는건가!?
------------------------------
Bitmap Image;
Image = new Bitmap (320,240);
Image에 대한 그래픽 처리 blah blah..
Response.Clear();
Response.ContentType="image/bmp";
Image.Save( Response.OutputStream, ImageFormat.Bmp);
Image.Dispose();
- 출력 오류 GDI+ 일반 오류 발생.
------------------------------
Bitmap Image;
MemoryStream memStream = new MemoryStream();
Image = new Bitmap (320,240);
Image에 대한 그래픽 처리 blah blah..
Response.Clear();
Response.ContentType="image/bmp";
Image.Save( memStream, ImageFormat.Bmp);
memStream.WriteTo( Response.OutputStream );
Image.Dispose();
- 성공
--
Trackback Address :: http://www.yuno.org/trackback/207
구글(Google)의 R&D 센터가 한국에 생긴다. 조금이라도 IT나 인터넷, 프로그래밍.. 이런 이야기에 관심이 있는 사람이라면 이런 저런 이야기가 많이 듣거나 봤을 것이다. 구글의 사무실 분위기 사원 복지 정책 등을 보고 있자면 소프트웨어 엔지니어의 천국이라는 생각이 들 정도이다.
많은 사람들의 구글의 채용 정책에 관심이 많고.. 또 많은 언론에서 구글의 독특한 채용 방법을 기사에 올리고 있다. 그러던 중에 우연히 매일 경제에 포스트 된 기사를 볼 수 있었다. 구글의 입사 시험 문제 중에 하나 였는데 사무실 옆 자리에 있는 형이 관심을 갖고 있는걸 보고 나도 한번 보게 되었다.
해당 기사 보러 가기 (이곳을 누르면 해당 기사로 갈 수 있습니다.)
" 양수 n에 대해서 1과 n 사이에 1이 나오는 횟수를 나타내는 함수를 f(n)이라고 한다. 예를 들어 f(13)=6이다. f(n)=n이 되는 첫번째 양수는 1이다. 두번째 양수는 무엇인가. "
풀어서 이야기 하면 양수 N 까지의 숫자를 나열 했을때 1 이 들어간 수를 헤아리는데 양수 N까지의 1의 갯수가 양수 N과 동일한 두번째 값을 찾는 문제이다.
단순하게 N까의 루프에서 1의 숫자를 헤아리게 만든다면 P4 3G 라도 엄청난 시간이 걸린다. -_-
한번쯤 관심이 있다면 풀어 보자~ << 풀어본 답 열기 >>
int N;
int Result;
int POW1, POW2, CNT; N = 1; // 양수 N 시작
while( 1 ) {
++N;
POW1 = 10;
POW2 = 1;
Result = 0;
while( N >= POW1 ) {
Result += (N / POW1 + 1) * POW2;
if( ((N / POW2) % 10) < 1 ) --CNT; POW2 = POW1;
POW1 *= 10;
}
if( N / POW2 == 1 )
Result += (N % POW2)+1; if( N == Result )
break;
} printf("The answer is %d",Result );
이 코드를 돌려보면 답은 199981이 나온다. 원래는 간단하다. N까지의 1의 숫자를 매번 헤아리는게 아닌 N까지의 숫자를 이용해서 1이 몇회 나오는데 자릿수 만큼의 루프를 통해서 찾아내는 방법으로 연산 횟수를 크게 줄이는 방식이다. N까지의 숫자를 나열했다고 치고 1의 자리수의 부터 N의 마지막 자릿수까지의 1의 출연 수를 계산해내는 방식으로 단순 처리 해보았다.
충분한 속도라 단순 등비 증가로 N에서 Result로 접근하지만 좀 더 가속 하고자 한다면 N과 f(N)은 사실상 계속 증가 할테니 N의 증가 폭을 2의 배수로 바꾸어 f(N)이 N을 넘게 되면 N/2 영역부터 중간점 찾기로 접근하면 연산수가 크게 줄 것 같다..;
--
Trackback Address :: http://www.yuno.org/trackback/180
많은 프로그래머들이 겪고 있는 신비로움일까. 아니면 무지에서 나오는 결과일까. 내가 속한 팀에서는 대부분 사내 테스트이 경우에는 디버그 버젼의 클라이언트를 이용해서 사용한다. 더 많은 클라이언트, 케릭터 정보, 많은 로그들을 얻을 수 있다는 장점이 릴리즈 버젼의 클라이언트 보다 더 크게 작용하기 때문이다.
그런데 여기서 가끔 문제가 생긴다. 릴리즈 버젼의 바이너리와 달리 디버그 버젼은 탱크와도 같아서 정말 튼튼하다. 프로그래머의 가벼운 실수 쯤은 가뿐히 넘어가 버린다.
많은 프로그래머가 실수하는 배열의 인덱스 오류로 인한 오버 플로우 정도는 디버그 컴파일시에 생기는 패딩 효과에 의해서 무마 되어 버린다.
그런데 오늘 정말 거지 같은 일이 있었다.
Windows API를 사용하는데 같은 클래스의 같은 메소드에서 Windows API의 결과가 릴리즈와 디버그가 달랐다. 한시간 넘게 고민하고 고쳐보고 구글질을 해봤지만 결국 원인 규명에는 실패. 참 난감하다.
이렇게 한참을 한 가지 문제를 해결 못해서 끙끙 거리다 보면, 어떤 책의 구절을 찾기 위해서 책장에서 책을 하나 하나 꺼내서 뒤지다가 없으면 책을 내동댕이 쳐서 엉망이 되어버린 방 처럼 머리속이 엉망이 된다.
그럴때는 지우개라도 사용해서 머리를 깨끗하게 비우고 다시 시작하고 싶다.
그리고, 많은 경우가 잠시 바람을 쐬고 처음 부터 차근 차근 하면 해결 되기도 한다. -_-
하지만. 오늘은 실패했다. ㄴㅁ..
--
Trackback Address :: http://www.yuno.org/trackback/176
|
|