Create TOC

2002년 10월 20일

Win32/Unicode Programming

1 Unicode 로 작성된 program의 동작 되지 않는 OS

  • Windows 95
  • Windows 98주1)

2 TCHAR의 형태를 활용한다.

UNICODE와 MBCS에서 같은 Source를유지하기 위하여 tchar.h의 TCHAR를 활용한다.

변경 전변경 후
charTCHAR
unsigned charTBYTE
char str[] = "test string"TCHAR str[] = TEXT("test string")

3 함수를 변경한다.

변경 전변경 후
strlen()_tcslen() * sizeof(TCHAR)
strcat()_tcscat()
strcpy()_tcscpy()
str...()_tcs...()

4 Unicdoe define 설정방법

_MBCS대신에 _UNICODE를 정의 한다. 일부 crt 프로그램의 경우 UNICODE define을 추가로 필요로 한다. MFC에서는 _UNICODE나 UNICODE중 하나만 define되어 있으면 된다.

5 MFC Application Unicode compile경우 link " _WinMain@16"의 Error가 발생한다.

Link->Output에서 Entry-point symbol 을 미지정 상태에서 ""에서 "wWinMainCRTStartup"으로 설정한다.

6 함수의 인자가 char*인경우

GetProcAddress(HMODULE, LPCSTR) 에서 두번째의 인자는 LPCSTR이므로 _T("")를 사용하지 않는다.

::GetProcAddress(hModuel, _T("function name"));  // no
::GetProcAddress(hModuel, "function name");      // ok

7 TCHAR의 형태를 지원하지 않는 함수 목록

atof (atoi, atol은 지원)

8 Unicode text file

unicode로 text파일을 만들때 편집기에 따라서 파일의 처음 부분에 2byte가 BOM 값을 가지는 경우가 있다. 따라서 text파일을 처리할 때 BOM 처리에도 주의해야 한다.

특별한 처리 없이 BOM이 있는 파일을 ftscanf 계열의 함수를 사용해서 처리하면 Unicode compile에서는

  • MBCS text 파일을 읽을경우 -> Unicode로 변경하여 read (O)
  • Unicode text 파일을 읽을경우 -> Unicode로 read (O)

MBCS compile에서는

  • MBCS text 파일을 읽을경우 -> MBCS로 read (O)
  • Unicode text 파일을 읽을경우 -> 잘못된 읽기(처음 한자만 읽고 다음 글자를 null로 인식하여, 한자만 읽힌다.)

결국 BOM이 포함된 text 파일을 다룰 경우 Unicode compile을 하는 것이 좋다.

9 9X 지원의 여부

9x에서는 Unicode 지원에 문제가 있다. 대부분의 Unicode 함수는 compile과 실행이 되나 return값이 FALSE가 나온다. 공통의 code에서는 LoadStringW(), CreateRegistryW()등이다. (windows api 계열)

wstrcpy()는 정상 동작한다. (c run-time 계열)

  • 1) 작동을 위하여서는 Windows program install이 필요함.

2002년 8월 20일

Visual C++ 6.0 버그

variable scope 버그

c++ spec을 지키지 않습니다.

for (int i = 0; i < 10; i++)
{
	; // nop
}

cout << i << endl;

이런 코드가 있을때 c++ spec 상 변수 ifor 범위 안에서만 유효해야 합니다. 따라서 cout 부분에서 에러가 발생해야 합니다.
하지만 VC 에서는 선언된 이후 계속 유효한 상태로 남아있습니다.

이 때문에 코드 이식성을 높이기 위해 위와 같은 문법을 사용할 수 없습니다.

기본 최적화에 따른 부동 소수점 연산 오류

아래 내용은 sleek에서 논의되었던 내용을 정리한것임.


다음과 같은 소스가 있습니다.

#include <stdio.h>

int main(int argc, char* argv[])
{
	float a = 0.433f;
	float b = a*1000.0f;
	int c = (int)b;

	printf("value %d \n",c);
	return 0;
}

이것을 실행해보면 432 가 나옵니다(VC++ 6.0 SP3, 4, 5 에서 확인함. gcc 에서는 433으로 잘 나옴).

반면

c = (int)(0.433 * 1000.0);

로 바꾸면 433으로 잘 나옵니다.

그리고 위의 소스를 다음과 같이 바꾸어도 433 이 잘 나옵니다.

#include <stdio.h>

int main(int argc, char* argv[])
{
	float a = 0.433f;
	float b = a*1000;

	float temp = 0.0f; // 쓸데없는 변수
	temp /= 100.0f;    // 쓸데없는 연산

	int c = (int)b;


	printf("value %d \
",c);
	return 0;
}

이렇게 되는 이유가 FPU 오차에 최적화 문제가 겹쳐서 발생되는 문제입니다.

float a = 0.433f;
float b = a*1000;
int c = (int)b;

여기서 a*1000는 FPU 레지스터에는 4.32999...e+0002 로 값이 나오는데, 이게 실제 메모리 b로 가면 4.33300..e+0002으로 라운딩이 됩니다.

근데.. 다음줄 c=(int)b;를 계산하려고 보니 b의 값이 아직 레지스터에 남아 있으니 4.3299...e+0002 를 가져다 계산하고 (int)변환은 라운딩 없이 무조건 잘라버리니까 결과는 432가 나오는데.. 즉 이건 c=(int)(a*1000); 과 동일한 값이 됩니다.

하지만 c=(int)b; 를 계산하기 전에 다른 코드가 있다면, 레지스터에 b를 다시 로드해야 되므로 메모리 b에 저장된 4.333000..e+0002를 먼저 FPU 레지스터에 불러들이고, 결과는 433이 나오게 됩니다.

정리해보면..

float a = 0.433f;
float b = a*1000;
int c = (int)b; // 이건 432
c= (int)b; // 이건 433
c= (int)(a*1000); // 이건 432
c= (int)b; // 이건 433

결국 Register에 있는 내용과 실제 메모리에 있는 내용이 다르면 당연히 메모리에 있는걸 읽어 와야 되는데 VC의 옵티마이저가 이걸 잘못 생각해서 errornous한 code 라고 생각하는 것 같습니다.

결국 MSDN 에 /Op 옵션을 사용하라고 나와있습니다.

PRB: Use /Op to Avoid Loss of Precision from Optimization

ps) VC.NET 에서는 이런 문제가 발생하지 않습니다 :)

Class view의 버그

아래 코드를 입력하고 class view 에서 확인해 보면 몇몇 함수는 제대로 표시되지 못합니다.

void a(int a, int b, int c);
void b(int a, int b = 1, int c = 1);
void c(int a, int b = 1, BOOL c = 1);
void d(int a, BOOL b = 1, BOOL c = 1);
void e(int a, BOOL b = 1, HKEY c = 1);

2002년 5월 14일

C++/Type Casting

C++에서는 C와 달리 casting 에 관련된 keyword가 다음 4가지가 존재하며, 각각 의미가 다르다. C++ 에서는 기존의 C-style casting 보다는 추가된 keyword를 사용한 casting을 권장한다.

static_cast

C-style casting과 동일하다. 따라서 C-style casting 에서 발생하는 모든 제약이 동일하게 적용된다.

이런 C 코드는

int firstNumber, secondNumber;
double result = ( ( double ) firstNumber ) / secondNumber ;

이런 C++ 코드로 바꿀 수 있다.

int firstNumber, secondNumber;
double result = static_cast <double> (firstNumber) / secondNumber ;

const_cast

const_cast는 표현식의 상수성(const)과 휘발성(violate)을 제거하는데 사용한다. 아래 예를 보면

class Root;
class Leaf : public Root;

Leaf       * pLeaf;
Leaf         Leaf;
const Leaf & cLeaf  = Leaf;
Root       * pRoot = new Leaf ;

pLeaf = &cLeaf;                        // error: 형이 다르다.
pLeaf = const_cast <Leaf *>(&cLeaf);  // ok.
pLeaf = (Leaf *) &cLeaf;               // ok. C-style casting.
pLeaf = pRoot;                         // error. 형이 다르다.
pLeaf = const_cast <Leaf *> (pRoot);  // error

dynamic_cast

상속 관계를 가진 경우에만 사용할 수 있다. 즉, 기본 class object 의 포인터(pointer)나 참조형(reference)을 파생(derived) class 또는 형제(sibling) class 타입으로 변환할 때 사용한다. 실패했을때는 NULL 리턴(포인터 변환시) 또는 exception 이 발생된다.

아래와 같은 경우에는 사용할 수 없다.

  1. 가상 함수가 없는 경우
  2. const_cast와 같이 상수성(const)을 제거하려는 경우.
class Root;
class Leaf : public Root;

Leaf       * pLeaf;
Root       * pRoot = new Leaf ;
Leaf         Leaf;
const Leaf & cLeaf  = Leaf;

Leaf       & rLeaf = dynamic_cast <Leaf &> (*pRoot); // ok.
pLeaf = pRoot;                                        // error. 형이 다르다.
pLeaf = dynamic_cast <Leaf *> (pRoot);               // ok.
pLeaf = dynamic_cast <Leaf *> (&cLeaf);              // error. const를 제거할 수 없다.

reinterpret_cast

포인터(pointer) 끼리의 형변환, 포인터를 정수형으로 형변환 그리고 정수형을 포인터로 형변환 할 때 사용된다. 서로 연관성이 없는 포인터(pointer)라고 해도 형변환이 가능하기 때문에 되도록이면 쓰지 않는 것이 좋다. casting 결과도 compiler 에 의존적이기 때문에 portable한 code 작성시 사용을 피하는 것이 좋다.

2002년 3월 8일

Debugging/Note

Debugging?

  • Debugger를 이용하여 문제를 해결하는 과정
    • Debugging 분류
    • Kernel mode debugging
      • Drivers, File Systems, Operation System components
      • 주로 BOD 발생
      • SoftIce나 WinDbg 원격 디버깅 추천
    • User Mode debugging
      • Crash, Hang, Leak,...
  • Debugging 방법
    • Dump debugging - dump file 분석해서 원인 찾기.
    • Interactive debugging - process 에 debugger 를 attach해서 실시간 디버깅.

보통 생각할 수 있는 문제 종류

  • Crash - 대부분은 AV(Access Violation), 젤 잡기 쉬움
  • Hang - CPU 100%, dead lock, I/O pending 등등
  • Leak - memory, resource 등등
  • 그외 논리 오류 등등

Debugging을 위해서 필요한것?

  • Debugger
  • symbol(보통 PDB)
  • source code

위에서부터 순차적으로 중요함. 심볼만 있어도 무지 편함. Release build 시에도 debug symbol을 생성하게 해두는것이 중요!!! 하다못해 map 파일이라도 만들것. map 파일이라도 있으면 address 로 부터 함수명을 찾을 수 있음.

Microsoft Debugging Tools

high cpu issue

  1. PerfMon 등으로 범인 thead 찾을것.
  2. userdump 들 이용해서 덤프떠서 확인하던지, debugger를 붙여서 해당 쓰레드를 조사할 것.

memory leak issue

Q268343 Umdhtools.exe: How to Use Umdh.exe to Find Memory Leaks

  1. PerfMon 으로 진짜 memory leak 인지 확인한다.
  2. GFlags 옵션을 조정한다.
  3. 재부팅
  4. DH -p <pid> -b -g -f dumpA.txt
  5. DH -p <pid> -b -g -f dumpB.txt
  6. 위 4, 5 식으로 여러개 뜬 다음
dhcmp dumpA.txt dumpB.txt result.txt

이런식으로 각 메모리 덤프를 비교해서 어딘지 찾아본다.

그외

Link

Q196755 HOWTO: Determine the Location of a Crash

VC 6.0을 사용할 때 유용한 매크로

C-runtime library를 msvcrt로 하고 release 에서도 PDB 만들고 모든 빌드에서 map 파일 만들게 한다.

Sub ProjectSettingStandard()
	'DESCRIPTION: 프로젝트 환경을 만든다.
	Dim proj
	Dim confs
	Dim config
	Dim release_map
	Dim debug_map
	Dim debug_name

	Set proj = ActiveProject
	Set confs = proj.Configurations

	' map 파일 이름을 만든다.
	release_map = "/map:" + Chr(34) + "Release/"+ proj.Name + ".map" + Chr(34)
	debug_map = "/map:" + Chr(34) + "Debug/"+ proj.Name + ".map" + Chr(34)

	' release, debug를 판단할 이름 생성.
	debug_name = proj.Name + " - Win32 Debug"

	' Application.Project.Configurations.Configuration.AddToolSettings "mfc", "2"
	' None : 0(FOR SDK), MFC Static Libraries : 1, MFC Shared Dll : 2

	If proj.Type = "Build" Then
		For i = 1 To confs.Count
			Set config = confs(i)
			' Common Settings

			' include path add
			config.AddToolSettings "cl.exe", "/I " + Chr(34) + "../inc" + Chr(34)

			' lib path add
			config.AddToolSettings "link.exe",  "/libpath:" + Chr(34) + "../lib" + Chr(34)

			' remove compiler options
			config.RemoveToolSettings "cl.exe", "/O2"
			config.RemoveToolSettings "cl.exe", "/ZI"

			' add compiler options
			config.AddToolSettings "cl.exe", "/Od /Gm /Zi"

			' add linker options
			config.AddToolSettings "link.exe", "/debug /OPT:REF"

			If config.Name = debug_name Then
				' Debug 일때
				config.AddToolSettings "cl.exe", "/MDd"
				config.AddToolSettings "link.exe", debug_map
			Else
				' Release 일때
				config.AddToolSettings "cl.exe", "/MD"
				config.AddToolSettings "link.exe", release_map
			End If
		Next
	End If
End Sub

알아두면 유용한 macro(C/C++)

  • __FILE__ : 현재 파일(VC, GCC)
  • __LINE__ : 현재 라인(VC, GCC)
  • __FUNCTION__ : 현재 함수(VC 에서 되는지 잘 모름. GCC 에서는 문제 없음)
  • NDEBUG : 릴리즈 빌드(VC, GCC)

2002년 1월 10일

Doxygen

Doxygen

Doxygen 는 C++, C, Java, IDL (Corba, Microsoft, and KDE-DCOP flavors), PHP, C# 을 위한 문서화 시스템이다.

source 에 남기는 주석을 이용하여 HTML, LaTex, RTF 포맷의 문서를 출력할 수 있다.

다운로드 및 설치

Doxygen

Doxygen site 에서 platform에 맞는 버전을 다운 받는다.

Graphviz

Doxygen 에서 document를 생성할 때 class 계층도나 파일 관계도를 그림으로 만들기 위해서 필요하다.

Graphviz site 에서 platform에 맞는 버전을 다운 받는다.

Html Help Workshop

Doxygen으로 생성한 html을 CHM 으로 만들기 위해서 Microsoft HTML Help Workshop 를 다운받는다.

DoxBar

Developer Studio 에서 Doxygen을 손쉽게 사용할 수 있는 add-on 모듈.

doxbar site 에서 다운 받을 수 있다.

설치는 doxbar site의 설명을 따라가면 된다.

간단한 주석 규칙

주석을 달때 몇가지 style 이 적용되지만 나는 javadoc 스타일을 선호한다. 아래 내용 이외에도 history, todo 에 관한 규칙도 있지만 따로 설명하지 않는다.

파일에 대한 주석

/**	@file [파일명]
	@brief [파일에 대한 간단한 설명]
*/

/*/////////////////////////////////////////////////////////////////////////////
// Include Headers
*/

/*/////////////////////////////////////////////////////////////////////////////
// Constants
*/

/*/////////////////////////////////////////////////////////////////////////////
// Declare Types
*/

/*/////////////////////////////////////////////////////////////////////////////
// Global Variable
*/

/*/////////////////////////////////////////////////////////////////////////////
// Declare Functions
*/

/*/////////////////////////////////////////////////////////////////////////////
// Implementation
*/

/*/////////////////////////////////////////////////////////////////////////////
// End of file
*/

함수에 대한 주석

/**	@brief [함수에 대한 간단한 설명]

	@param[in] [파라미터 설명]
	@param[out] [파라미터 설명]
	@param[in,out] [파라미터 설명]

	@return [Return Values]

	@remark [Remarks]

	@sa [See Also]

	@note [Note]
*/
int hello(int x)
{
...
}

class에 관한 주석

/**	@brief [class 에 대한 간단한 설명]
*/
class Foo
{
...
};

struct에 관한 주석

/**	@brief [struct 에 대한 간단한 설명]
*/
struct foo
{
...
};

class, struct 멤버 변수, 일반 변수에 관한 주석

int i;	/**< loop counter */

매크로에 관한 주석

/**	@brief [macro 설명]
*/
#define FOO(x)   do { ... } while (0)

Group

/// @defgroup GROUPNAME description
//@{

/** @defgroup SUBGROUP1 desc
@ingroup GROUPNAME
*/
//@{
...
//@}

/// @name group name
//@{
...
//@}

//@}

유용한 plugin

DoxygenToolkit

VIM용 plugin. 함수 주석을 편리하게 달 수 있다.

아래는 내 vimrc에서 DoxygenToolkit용 설정

"for DoxygenToolkit.vim
let g:DoxygenToolkit_interCommentTag="^I"
let g:DoxygenToolkit_blockHeader=""
let g:DoxygenToolkit_blockFooter="^I@warning^I^M^M^I@remarks^I^M^M^I@note^I^M^M^I@sa^I^M"
let g:DoxygenToolkit_authorName="Yun-yong Choi"

실제 설정에 적용할때는 ^I는 tab을 누르면 되고 ^M은 ^V를 누르고 엔터키를 누르면 된다.

플러그인은 http://www.vim.org/scripts/script.php?script_id=987 에서 받을 수 있다.