Create TOC

2002년 10월 20일

Win32/Unicode Programming

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

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

TCHAR의 형태를 활용한다.

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

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

함수를 변경한다.

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

Unicdoe define 설정방법

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

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

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

함수의 인자가 char*인경우

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

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

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

atof (atoi, atol은 지원)

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을 하는 것이 좋다.

9X 지원의 여부

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

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

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 에서 받을 수 있다.