Visual Studio는 C/C++을 싫어해 (?)

개발 2011.02.27 12:58
 이번에 새로운 프로젝트를 세팅하면서 해보려고 했던 것 중 하나가 비쥬얼 스튜디오의 텍스트 파일의 기본 인코딩 방식을 CP949에서 UTF-8로 바꾸는 것이었다. (여기에서 뭔가 이상함을 느끼는 사람들도 있을텐데, 일단 계속 읽어주시길 바란다.)

 내가 개인적으로 작성하는 코드 대부분은 가급적 영어로만 작성하려고 하기 때문에 유니코드를 고집해야 할 절체절명의 이유는 없다. 하지만 앞으로도 한글을 전혀 적지 않는다는 보장은 없는 법이며, 가끔 가다 ASCII 영역 이외의 문자 및 인코딩 방식을 사용해서 컴파일에 문제가 발생하는 오픈 소스 프로젝트 덕분에 귀찮음을 겪은 적이 있었다. 그래서 발생하지 않을만한 경우까지 고려하는 성급한 일반화 Premature generalization를 하는 안 좋은 버릇이 발동한 것이다.

 사실 비쥬얼 스튜디오에서 저장하려는 파일의 인코딩을 바꾸는 것은 그리 어려운 일이 아니다. 버젼마다 방법이 약간 다르긴 한데, 2010에서는 File - Advanced File Options...을 택하면 현재 편집 중인 텍스트 파일의 인코딩 및 개행 방식을 바꿀 수 있다. 다만 이게 파일 한 개 단위로 적용이 되기 때문에 프로젝트 전체에 대해 일괄적인 변경을 가한다거나 하는 작업은 불가능하다. 내가 바라는 것은 솔루션/프로젝트 단위로 적용할 수 있는 파일 정책인 것이다.

 그리하여 열심히 웹을 뒤지다 보니 놀라운 문구를 하나 발견할 수 있었다.


Visual Studio saves files in UTF-8 encoding by default, which is a Unicode-based format and should be handled appropriately by most computers.

VS2010: Unicode file saving | Microsoft Connect
 
???!!!


 ... 그렇다면 그간 CP949로 저장되어 온 수 많은 나의 .cpp, .h 파일들은 대체 어떻게 된 것인가? 하지만 내 실수가 있을 수도 있기에 아래의 단계를 밟아 다시 한 번 검증해 보기로 하였다.

  1. 새로 프로젝트를 만든다.
  2. 한글을 포함하는 소스 파일을 만든다.
  3. 저장한다.
  4. 인코딩 방식을 확인한다.
 아니나 다를까 역시나 CP949로 저장이 되어 있다. 하지만 이 것만 가지고 무려 Microsoft 공식 답변의 권위를 무시하기는 어려웠기에 조금 더 정확한 정보를 얻기 위해 다른 정보를 찾아보았고, 똑같은 사이트에서 무언가 수상한 문구를 하나 찾을 수 있었다.


We did switch to using UTF-8 w/BOM from VS 2008 onwards,
with a few exceptions for Native Tools.

Source FIle Encoding. | Microsoft Connect


 즉, 2008에서 만든 네이티브 프로젝트에선 사용자 컴퓨터의 로케일에 맞는 지역 인코딩을 기본으로 택한다는 이야기이다.[각주:1] 그렇다면 애초에 유니코드가 기본인 CLI 계열의 프로젝트는 이미 UTF-8로 잘만 저장을 해왔다는 것이다... 이 충격적인 진실을 확인하기 위해 기존에 만든 C# 소스 파일들을 뒤져보니 역시나 UTF-8 with BOM으로 저장되어 있었다. orz

 요약하자면,
  • VS 2008까지는 일부 네이티브 프로젝트의 텍스트 파일은 UTF-8이 아닌 시스템 로케일에 따른 인코딩 방식으로 저장된다.
  • VS 2010에서는 모든 텍스트 파일의 기본 인코딩 방식은 특별히 명시하지 않는 한 UTF-8 with BOM이다.
  • 그런데 네이티브 프로젝트들은 여전히 시스템 로케일에 따라 인코딩 방식이 선택된다. orz
  • 따라서 우리는 MS가 이런 간단한 문제조차 신경쓰지 않을 정도로 네이티브 개발자들을 홀대한다는 결론을 내려볼 수 있다. (??)

 

  1. 물론 위의 답변에서 VS 2010은 자동으로 UTF-8을 택한다고 했으니 이론적으로 VS2010에서는 네이티브 프로젝트라고 해도 UTF-8로 저장되는게 맞다. [본문으로]

'개발' 카테고리의 다른 글

NDC 간략한 참관기 #1  (0) 2011.06.04
기본 타입 간 (무분별한) 변환은 자제합시다  (3) 2011.05.14
Visual Studio는 C/C++을 싫어해 (?)  (3) 2011.02.27
Bitwise Switch  (2) 2011.02.19
std::unique_ptr  (0) 2010.04.26
RVO와 NRVO  (0) 2010.04.26
Trackbacks 0 : Comments 3

Bitwise Switch

개발 2011.02.19 01:56

 코딩하다 비트셋을 쓰면 아래 같이 각 플래그들을 검사해서 해당되는 경우 관련 작업을 수행하는 식의 코드를 줄줄이 작성하는 경우가 가끔 있다.


if (bitset & FLAG_A) {
	/* A 플래그와 관련된 작업들 */
}

 ...

if (bitset & FLAG_Z) {
	/* Z 플래그와 관련된 작업들 */
}


 오늘 위와 같은 코드를 보다가 아래처럼 비트셋에 적용되는 switch 문이 있으면 좋겠다는 생각이 들어서 검색해보았는데 못 찾아서 (그 이유는 아래에서 밝혀진다 -_-) 직접 만들어 보기로 하였다.


bitwise_switch(bitset) {
	case FLAG_A: /* A 플래그와 관련된 작업들 */

	...

	case FLAG_Z: /* Z 플래그와 관련된 작업들 */
}


 사실 구현 자체는 그리 어려운게 아니지만 비트 연산을 이용한 몇 가지 트릭이 필요했다. 일단 코드는 아래와 같다.

 
// Calculate LSB index in O(1) using de bruijn sequences.
// For more information, read "Using de Bruijn sequences to index a 1 in a computer word"
inline const int GetLSBIndex(const uint32_t LSB)
{
	static const uint32_t DeBruijnConstant32 = 0x077CB531U;
	static const int32_t HashTable[32] =
	{
		 0,  1, 28,  2, 29, 14, 24,  3,
		30, 22, 20, 15, 25, 17,  4,  8,
		31, 27, 13, 23, 21, 19, 16,  7,
		26, 12, 18,  6, 11,  5, 10,  9
	};

	return HashTable[static_cast(LSB * DeBruijnConstant32) >> 27];
}

inline const uint32_t ExtractLSB(uint32_t& bitset)
{
	uint32_t LSB = bitset & (-bitset);
	bitset = bitset ^ LSB;
	return LSB;
}

#define bitwise_switch(bitset) \
	for (uint32_t __bitset__ = bitset; __bitset__ != 0;) \
		switch (GetLSBIndex(ExtractLSB(__bitset__))) \


 동작은 아주 간단하다. 주어진 비트셋에서 Least Significant Bit를 추출한 뒤 추출한 LSB의 위치를 계산, 이 값을 switch-case 문으로 넘기는 것을 반복하는 것이다.
 
 위 코드에서 함수 ExtractLSB는 주어진 비트셋에서 LSB를 뽑아내는 비트 연산 기법인 bitset & (-bitset)을 구현한 코드로 쉽게 이해할 수 있다. 함수 GetLSBIndex는 주어진 LSB 값을 받아 오프셋을 계산하는 함수인데, 이를 위해 De Bruijn 수열을 이용, Perfect hash function을 만들어 사용한다. 알고리즘의 원리가 궁금하신 분들은 이 논문을 참조하시면 되겠다. 비트 값을 굳이 이렇게 오프셋 값으로 바꿔서 사용하는 이유는 switch-case 분기문에 주어지는 인자가 연속된 정수 값인 경우 컴파일러가 이를 점프 테이블을 이용하는 간접 분기문으로 바꾸는 최적화를 해주기 때문이다.


 최적화가 목표였던 건 아니지만 그래도 각각의 비트에 대한 분기 처리가 O(1)에 처리되도록 신경을 썼으니 처리 속도가 궁금해진다. 그렇다면 이 기법을 이용하는 경우 속도가 얼마나 빨라질까? 불필요한 비트 검사가 생략되니 빨라질 수도 있겠지만, 비트 하나를 검사하는데 들어가는 비용이 확연히 커졌으니 느려질 수도 있다. 과연 결과는?


열기



 오늘의 교훈: 최적화 같은거로 고민하지 말자. 최적화로 얻는 시간보다 최적화에 들이는 시간이 더 많을거다. -_-;;

'개발' 카테고리의 다른 글

기본 타입 간 (무분별한) 변환은 자제합시다  (3) 2011.05.14
Visual Studio는 C/C++을 싫어해 (?)  (3) 2011.02.27
Bitwise Switch  (2) 2011.02.19
std::unique_ptr  (0) 2010.04.26
RVO와 NRVO  (0) 2010.04.26
volatile과 메모리 배리어  (2) 2009.11.11
Trackbacks 0 : Comments 2