예전에 본인이 글로 쓴 적도 있고, 상식 차원에서 이미 아시는 분도 있겠지만..
프로그래밍 언어마다 문자열을 다루는 방식엔 차이가 존재한다.
C/C++은 null-terminated 문자열이라는 단순하고 독특한 체계를 사용하는 반면, 다른 언어들은 그렇지 않다.
그렇기 때문에, 문자열 상수가 실행 파일 내부에 어떤 형태로 박혀 있는지를 추적하면, 이 프로그램이 무슨 언어로 만들어졌겠는지 추측이 어느 정도 가능하다.

과거의 도스 시절에는 볼랜드 사에서 개발한 터보 시리즈의 컴파일러가 인기가 많았다. C/C++과 파스칼이 기억에 남는다. 이 볼랜드 제품은 당시 타사의 컴파일러가 제공하지 않던 두 가지 독자적인 기능이 있었다. 하나는 깔끔하게 잘 만들어진 IDE(에디터)였고, 다른 하나는 BGI(볼랜드 그래픽 인터페이스)라고 일컬어지는 그래픽 API였다.

한 IDE에서 프로그램을 바로 빌드-실행-디버그할 수 있으니 프로그램 개발 생산성이 뛰어나고 굉장히 편리하다. 이에 덧붙여, 그래픽은 그렇잖아도 printf 같은 표준화된 API 규격이 전무해서 ‘싸제’ 라이브러리에 의존할 수밖에 없던 영역인데, 자체 개발 라이브러리가 있다 보니 볼랜드의 컴파일러는 폭발적인 인기를 모을 수밖에 없었다.
bgidemo라고 유명한 그래픽 API 예제 프로그램도 있었는데 기억하는 분이 있으려나 모르겠다. QBasic용 예제 프로그램인 nibbles, gorilla 게임과 비슷한 시기에 만들어진 그 시절 추억이다.

사용자 삽입 이미지

아래의 스크린샷은 이 BGI 라이브러리를 사용해서(=링크해서) 만들어진 어느 EXE 파일 내부를 들여다본 모습이다. 그래픽 라이브러리이다 보니 내부적으로 출력하는 에러 메시지 문자열, 가령 No error, (BGI) graphics not installed, 심지어 Out of memory in flood fill 같은 친숙한 문자열이 내장되어 있음을 알 수 있다. 그런데 동일한 문자열들 사이에 한 놈은 ▲, →, ← 같은 이상한 기호가 듬성듬성 끼어 들어가 있다. 왜 그럴까?

사용자 삽입 이미지

사용자 삽입 이미지

기호가 없는 프로그램은 C언어(=터보 C)로 만들어진 프로그램이다. 왼쪽의 16진수값을 보면 알겠지만, 이들은 모든 문자열들이 그냥 0번 문자로 구분되어 있다.
그러나 기호가 있는 프로그램은 파스칼로 만들어진 프로그램이다. ▲, →, ←은 다음에 뒤따르는 문자열의 길이를 의미한다. 예를 들어 “▲Graphics hardware not detected”를 보면 ▲의 코드 번호는 0x1E, 즉 30인데 그 에러 메시지의 길이는 30바이트임을 알 수 있다. 얘네는 반대로 문자열들 사이에 0번 문자가 전혀 존재하지 않는다.

실제로 C/C++ 말고 String이 built-in type으로 존재하는 언어들은 이렇게 글자 수를 따로 저장해 놓는 방식으로 문자열들을 관리한다. 베이직으로 만들어진 프로그램도 QuickBasic이든 PowerBasic이든 문자열 상수들을 들여다보면 비슷한 결과를 얻을 수 있다. 그래서 이런 언어는 문자열의 길이를 구하는 함수의 시간 복잡도가 O(1)인 반면, C언어만 strlen의 시간 복잡도는 O(n)이다.

베이직 언어들은 문자열의 길이가 16비트 정수로 저장되던 반면, 터보 파스칼은 문자열 길이를 달랑 8비트 크기로 저장하여, 문자열의 길이가 256자를 넘을 수 없다는 한계가 존재했다. 흠;;

파스칼로 만든 프로그램을 들여다보면 Runtime error 같은 문자열도 존재한다. 이 역시 C/C++로 만들어진 프로그램에서는 디버그 빌드가 아닌 이상 있을 수 없는 개념이다. C/C++은 배열 첨자 범위의 검사조차도 안 할 정도로 런타임 에러라는 개념 자체가 존재하지 않는-_- 언어이기 때문이다. 그저 컴퓨터 다운(도스 시절)이 아니면 segmentation/page fault(요즘 같은 보호 모드 운영체제에서)-_-만이 존재할 뿐. -_-;;

그 반면, %d, %s이라든가 Null pointer assignment 같은 문자열이 있다면 그건 99.9% C 라이브러리가 들어갔다는 뜻이고 그 프로그램은 C/C++로 작성되었다고 유추할 수 있다.

덧붙이는 말

1. 볼랜드는 BGI 라이브러리만큼이나 텍스트 모드용 GUI? TUI? 툴킷으로 Turbo Vision이라는 라이브러리를 개발한 것으로도 유명했다. MS가 도스용 비주얼 베이직을 잠시나마 개발했다면 볼랜드에는 이런 게 있었던 셈. 당장 터보 C++과 파스칼의 IDE부터가 이를 사용해서 개발되기 시작했다. 비록 C/C++과 파스칼에서 모두 지원되긴 했지만 이 언어의 주 개발 및 지원 언어는 파스칼이었지 싶다. MS가 베이직을 좋아한다면, 볼랜드는 전통적으로 파스칼을 더 좋아하는 회사였다. (그러니까 훗날 델파이까지 만들었지)

지금은 세월이 세월이다 보니 소스가 완전히 풀려서 이이 프로젝트는 오픈소스 진영에서 관리되고 있다. 내 기억이 맞다면 DJGPP의 IDE인 Rhide가 이 Turbo Vision의 오픈소스 버전으로 개발되었다.
그리고 우리나라에서 PC 경진대회가 정보 올림피아드로 최초로 바뀌었던 1996년(13회), 대회의 채점 프로그램이 Turbo Vision 기반으로 개발되어 있던 걸 본인은 분명히 봤다.

2. 오늘날 윈도우용 네이티브 EXE/DLL이 만들어지는 출처는, 내 감으로는 비주얼 C++이 적게 잡아도 70% 이상, 그 뒤에 소수의 오픈소스 프로젝트용으로 gcc, 그리고 끝으로 델파이 정도가 고작인 것 같다. 볼랜드는 그 후로 다른 회사에 인수되면서 이름도 여러 번 바뀌고(InPrise, CodeGear, Embarcadero 등...;;) 우여곡절을 많이 겪었는데 걔네 입장에서는 옛날의 영광이 그리울 법도 할 것 같다.

3. BGI 라이브러리와 파워베이직--얘 역시 전신이 볼랜드 사의 터보 베이직이긴 했지만--의 그래픽 라이브러리는 이상하게도 VGA mode 13h를 지원하지 않아서 개인적으로 아쉬웠었다. (퀵베이직은 지원했는데...) 해상도가 너무 낮아서 한글· 한자 같은 문자를 찍는 데는 부적격이었지만 256색 덕분에 게임 만들 때는 필수이던 그래픽 모드이다. 그게 지원됐으면 그 당시 게임 만들기가 훨씬 더 수월했을 텐데 말이다.

Posted by 사무엘

2011/07/15 08:38 2011/07/15 08:38
, , ,
Response
No Trackback , 2 Comments
RSS :
http://moogi.new21.org/tc/rss/response/540

아래 코드를 실행하면 놀랍게도..
입력된 숫자에 대한 팩토리얼 값이 출력된다. 단, 플랫폼은 x86 한정으로..;;
(뭐, 컴퓨터가 돌아가는 원리를 아는 사람이라거나 맨날 기계어 코드 들여다보는 게 직업인 사람이라면 전혀 새삼스러운 일이 아니겠지만)

비주얼 C++뿐만이 아니라 도스용 DJGPP로도 프로그램이 잘 동작하는 걸 확인했다. 둘 다 IA32 아키텍처인 건 동일하니까 이는 당연한 귀결.

int main(int argc, char* argv[])
{
 BYTE instrs[]={
  0x55,
  0x8B,0xEC,
  0x83,0xEC,0x08,
  0xC7,0x45,0xFC,0x01,0x00,0x00,0x00,
  0x8B,0x45,0xFC,
  0x89,0x45,0xF8,
  0xEB,0x09,
  0x8B,0x4D,0xF8,
  0x83,0xC1,0x01,
  0x89,0x4D,0xF8,
  0x8B,0x55,0xF8,
  0x3B,0x55,0x08,
  0x7F,0x0C,
  0x8B,0x45,0xFC,
  0x0F,0xAF,0x45,0xF8,
  0x89,0x45,0xFC,
  0xEB,0xE3,
  0x8B,0x45,0xFC,
  0x8B,0xE5,
  0x5D,
  0xC3
 };

 PVOID pv = instrs;
 
int (*pfn)(int) = reinterpret_cast<int (*)(int)>(pv), y;
 while(1) {
  printf("? "); scanf("%d", &y); if(y<1) break;
  printf("%d\n", pfn(y));
 }
 return 0;
}


프로그래밍 언어의 인터프리터 내지 just-in-time 컴파일러를 만든다거나,
가상 기계 에뮬레이터를 만드는 것은 어렵지 않다.
결국 핵심이 뭐냐 하면 컴퓨터가 직통으로 실행하는 코드 자체를 실행 시간에 메모리에다 생성하는 건데,
함수 포인터가 가리키고 있는 게 바로 저런 것들이다.

물론, 위에서처럼 실행해야 할 코드가 완전히 고정돼 있는 경우라면
소스 코드에다 인라인 어셈블리를 집어넣으면 되겠지만, 그 코드는 데이터 영역에 들어가는 게 아니라 소스 코드(text) 영역에 그대로 포함되어 버리게 될 것이다.

위의 팩토리얼 함수는 물론 컴파일러가 생성해 준 코드를 복사한 것이다.
최적화를 안 한지라, 간단한 for 루프 하나밖에 없는 함수가 코드 길이가 꽤 길다.
최적화를 하고 나면 정상적인 함수 입출력 껍데기에 해당하는 코드조차도 거추장스러운지 생성되지 않아서
그걸 저렇게 따로 떼어내서 쓸 수가 없었다.

Posted by 사무엘

2011/07/08 08:06 2011/07/08 08:06
, ,
Response
No Trackback , 3 Comments
RSS :
http://moogi.new21.org/tc/rss/response/537

C/C++로 프로그램을 개발하는 과정에서 아주 난감해지는 경우 중 하나는, 바로 Debug 빌드와 Release 빌드의 실행 결과가 서로 다를 때이다. 개발 중이던 Debug 빌드 스냅샷에서는 잘만 돌아가는 프로그램이 정작 최적화된 Release 빌드에서는 이따금씩(항상도 아니고!) 에러가 난다면?

이런 버그는 문제를 찾아내려고 정작 디버거를 붙여서 실행할 때는 재연되지 않는 경우가 태반이어서 프로그래머를 더욱 애먹인다. 특히 복잡한 멀티스레드와 관련된 버그라면 그저 묵념뿐..;; 하지만 그런 특수한 경우가 아니라면, Debug와 Release의 실행 결과가 다른 이유는 본인의 경험상 거의 대부분이 초기화되지 않은 변수 때문이었다.

비주얼 C++은 Debug 빌드에서는 초기화되지 않은(공간 확보만 해 놓고 프로그램이 아직 건드리지는 않은) 메모리의 영역을 티가 나는 값으로 미리 표시도 해 놓고 아주 특수하게 취급해 준다. 메모리를 할당해도 좌우에 여분을 두고 좀 넉넉하게 할당하며, 때로는 그 넉넉한 여분 공간의 값이 바뀐 것을 감지하여(바뀌어서는 안 되는데) 배열 첨자 초과 같은 에러를 알려 주기도 한다. 프로그래머의 입장에서야 이건 꽤 유용한 기능이다.

그러나 Release 빌드에는 이런 거추장스러운 작업이 물론 전혀 없다. 그러니 메모리 범위를 초과한다거나, 읽어서는 안 되는 엉뚱한 주소의 메모리로부터 값을 읽거나, 올바른 영역이더라도 초기화되지 않은 쓰레기 값을 얻었을 때의 결과는 두 빌드가 서로 극과 극으로 달라질 수밖에 없다.

이렇게, 빌드 configuration에 따라 동작이 달라지는 코드는 두말 할 나위도 없이 결함이 들어있는 faulty 코드이다. 이런 코드에서 문제의 원인을 찾는 건 극도로 어려운 일이다. 서울에서 김 서방 찾기, 모래사장에서 바늘 찾기, 사격장에서 흘린 탄피 찾기가 따로 없다. ㅜㅜ 자기가 짠 코드에서 결함을 찾는 것도 어려워 죽겠는데 하물며 회사 같은 데서 남이 짠 faulty 코드를 인수인계 받았다면... -_-;;;

(본인이 다니던 모 병특 회사에서 본인의 직속 상사는 이렇게 말했다. “그런 코드를 짜는 건 프로그래밍을 하는 게 아니라 똥을 싸는 거다.” 공감한다. -_-)

C/C++은 물론 간단한 지역 변수에 대해서야 ‘이 변수를 초기화하지 않고 사용했습니다’ 같은 지적을 컴파일 시점에서 해 준다. 그러나 복잡한 포인터나 배열로 가면 일일이 그 용법이 올바른지 컴파일 시점에서 판단하지는 못한다. 그저 프로그래머가 조심해서 코드를 작성하는 수밖에 없다.

이와 관련된 본인의 경험을 소개하겠다.
꽤 옛날에 짜 놓은 비주얼 C++ MFC 기반 GUI 프로그램 소스의 내부에서, 핵심 알고리즘만 떼어내서 다른 콘솔 프로그램에다 붙여야 할 일이 있었다.
그 당시에는 나름 구조적으로 프로그램을 만든 것이지만, 지금 관점에서 모듈간의 cohesion은 여전히 개판오분전이었던지라 상당수의 코드를 리팩터링해야 했다.

그래서 코드를 붙였는데, 원래의 GUI 프로그램에서는 잘 돌아가던 코드가 새로운 프로젝트에서는 얼마 못 가서 뻗어 버렸다. Debug 빌드와 Release 빌드의 실행 결과가 다른 건 두말 할 나위도 없거니와, 심지어 같은 Release 빌드도 F5 디버거를 붙여서 실행하면 별 탈이 없는데 그냥 실행하면 뻗었다! 이건 스레드 쓰는 프로그램도 아닌데! 이거야말로 제일 골치 아픈 경우가 아닐 수 없었다.

Debug 빌드는 Release 빌드보다 워낙 느리게 돌아가고, Release 빌드도 디버거를 붙였을 때와 그렇지 않았을 때 성능이 살짝 달라진다. 그러니 앞에서 언급했듯이 스레드 관련 race condition은 영향을 받을 수 있다. 하지만 그런 것도 아니라면? 의심스러운 배열은 무조건 다 0으로 초기화하고, 혹시 내가 리팩터링을 하면서 실수를 하지는 않았는지 몇 번이나 꼼꼼이 살펴봤지만 문제는 눈에 띄지 않았다.

별 수 있나. printf 로그를 곳곳에다 박아 넣어서 의심스러운 부분을 추적한 뒤 다행히 문제를 찾아냈다.
게임 같은 리얼타임 시스템에서는, 심지어 디버그 로그 찍는 코드만 추가해도 버그가 쏙 숨바꼭질을 해 버리는 막장 중의 막장 경우도 있다만 내 프로그램은 그런 정도는 아니어서리..;;

사실은 기존 GUI 프로그램에서 돌아가던 코드에서부터 문제가 있었다.
배열을 선언했는데, 0~1번 인덱스에 접근할 일이 없어서

ptrData = new char[100];
ptrData-=2;

같은 잔머리를 굴려 줬던 것이다. 요런 짓을 옛날에 Deap 자료구조를 구현할 때도 했던 것 같다.
그러니 이 포인터로는 0과 1번 인덱스를 건드리지 않아야 하는데...
그런데 그것이 실제로 일어났습니다. ㄲㄲㄲㄲㄲ

그 허용되지 않는 메모리의 상태가 GUI 프로그램과 콘솔 프로그램, 심지어 같은 프로그램도 Debug와 Release, 디버거 붙이냐 안 붙이냐 여부에 따라 싹 달라져서 나를 골탕먹였던 것이다. 예전에는 수 년째 아무 탈 없이 잘 돌아가던 코드가 말이다.
저런 간단하고 고전적인 배열 첨자 초과 문제가 이런 결과를 야기할 줄 누가 알았을까?

C/C++은 내가 짠 코드를 내가 완전히 책임질 수 있고 컴퓨터 관점에서의 성능· 능률· 최적화가 중요한 해커나 컴덕후에게는 가히 환상적인 언어이다. 이보다 더 좋을 수가 없다. 예전에 내가 비유했듯, 세벌식이 기계 능률과 인체 공학적인 특징을 잘 살린 것만큼이나 이 언어는 고급 언어의 특성과 기계적인 특성을 꽤-_- 잘 절충했다.

그러나 언어의 구조적으로 가능한 무질서도가 너무 높은 것도 사실. C/C++가 까이는 면모 자체가 크게 (1) 언어 자체의 복잡도 내지 결함 그리고 (2) unmanaged 환경이라는 여건 자체라는 두 갈래로 나뉘는 양상을 보인다. 오늘날의 소프트웨어 시스템에서 프로그래밍 언어는 모름지기 수십, 수백만 줄의 프로젝트에서 살인적인 복잡도를 제어 가능해야 하고, 작성한 코드의 최소한의 품질과 안전성이 보장되어야 하며, 또 무엇보다도 빨리빨리 빌드가 돼야 하는데 C/C++은 영 한계를 보이기도 한다.

뭐, 그래도 이미 C/C++로 작성된 코드가 너-_-무 많고 그것도 다들 중요한 저수준 계층에 있다 보니, 이 언어가 쉽게 없어지지는 않을 것이고 특히 C++은 몰라도 C는 절대 안 없어지리라.. ㅋㅋ 프로그래밍 언어의 라틴어급.

C/C++과는 전혀 다른 언어이다만, 과거엔 QuickBasic도 IDE에서 돌리는 프로그램과, 실제로 컴파일-링크를 한 EXE의 실행 모습이 대동소이하게 달라서 프로그래머를 애먹이기도 했다. 물론 이건 C/C++에서의 Debug/Release와는 다른 양상 때문에 차이가 나는 경우이다.
결론은, 프로그램 작성하다가도 틈틈이 Release 형태로 최종 결과물을 확인하는 게 필요하다. ^^

Posted by 사무엘

2011/06/22 08:23 2011/06/22 08:23
,
Response
No Trackback , 6 Comments
RSS :
http://moogi.new21.org/tc/rss/response/529

C/C++에는 ? : 라는 독특한 연산자가 있다. A ? B: C꼴로 표현되어 피연산자가 3개나 붙는 유일한 연산자이다.
이 연산자의 역할은 매우 단순하다. A가 참이면 연산자의 값은 B가 되고, 그렇지 않으면 C가 된다. 그래서 아예 if문의 역할을 간단히 대신할 수도 있으며, 콤마 연산자와 결합하면 어지간한 함수 호출마저도 한 연산식에다 박아 넣을 수 있다. 다만, 그게 너무 사악하다고 여겨졌는지-_-, C# 언어에는 콤마 연산자가 사라지고 콤마는 for 키워드 안에서만 제한적으로나 허용되지 싶다.

? : 는 &&, || 와 마찬가지로 C/C++에서 단축연산이 적용된다. A && B에서 A가 거짓이면 B는 실행이 전혀 되지 않고 전체 결과가 거짓이 되며, A || B에서 A가 참이면 B는 실행되지 않고 바로 전체 결과가 참이 된다. 그런 것처럼 ? :는 선택되지 않은 항에 대해서는 당연히 연산이 일어나지 않는다.

<날개셋> 한글 입력기는 짝퉁 C언어 문법 수식 해석기를 내장하고 있기 때문에, 이를 이용해 글쇠, 오토마타, 글자판 전환 글쇠 등에서 문자 입력 시스템의 자유도를 굉장히 높일 수 있다. 비록 튜링 완전한 수준은 못 돼도 말이다. 이때에도 ? : 연산자는 물론 매우 요긴하게 쓰인다.

? : 는 좌결합이 아니라 우결합이다. A ? B : C ? D : E는 (A?B:C) ? D : E가 아니라 A ? B : (C?D:E)로 결합한다. 그러므로 전자처럼 쓰려면 괄호를 넣어 줘야 한다.

? : 는 다른 연산 구문들을 포함하는 if문 대용처럼 쓰이는 만큼, 연산자의 우선순위가 상당히 낮다. 다른 평범한 연산자들이 다 결합한 뒤 나중에야 적용된다. 그게 합리적이다.
그러나 얘도 콤마와 대입 연산자보다는 순위가 높다. 그렇기 때문에 A = B ? C : D 라고 써 주면 알아서 A = (B?C:D)로 해석되어, A에는 B 조건의 충족 여부에 따라 C 아니면 D가 대입된다.

반대로, ? : 의 내부에 콤마 연산이나 대입 연산이 포함되어야 한다면 이들 연산은 무조건 괄호로 싸야 한다.

A ? (B=2): (C=5)
B에다가 괄호를 안 하면 = 가 ?와 :를 둘로 쪼개 버리는 효과가 나기 때문에 에러가 발생한다.
그리고 C에다가도 괄호를 생략할 수 없는데, 괄호를 안 하면 연산의 의미가 (A?(B=2):C)=5가 되어 버리기 때문이다. 우선순위의 특성상, =가 C항이 아니라 ? = 전체와 대응한다는 뜻 되겠다.

그리고 또 생각해 볼 것은, ? : 연산자의 값은 L-value가 될 수 있겠냐는 점이다. (대입 가능하겠냐)
<날개셋> 한글 입력기는 수식이 처음 도입된 3.0 이래로 지금까지 (조건 ? A:B)=100 과 같은 구문이 지원된 적은 없다. 그러나 이제 <날개셋> 6.0 이후의 다음 버전부터는 그게 가능해진다. 단, 2항과 3항 중 하나라도 변수에 연산자가 조금이라도 붙어서 A+2, -B 같은 형태가 되면 L-value 원칙이 깨지게 되는데, 그런 오류는 수식 입력 시점에서 프로그램이 자동으로 감지해 준다.

이게 지원되면 조건 ? (A=100): (B=100)보다야 구문을 더욱 간단하게 만들 수 있으니까 사용자의 입장에서 좋을 것이다. 더구나 콤마 연산자도 최후의 항의 변수 정보를 남겨 주기 때문에 (조건 ? (A=100,C): (B=50,D)) +=20 같은 복잡한 대입도 가능해진다. 저 식의 의미는 무엇일지 독자 여러분이 생각해 보기 바란다.

정작 이 연산자에서는 괄호가 필요하지 않다. 조건 ? A:B=100 이라고 하면 (조건 ? A:B)=100이 되며, 100 대입 연산은 3항의 B에만 연결되는 게 아니라 ? : 연산의 결과 전체에 걸린다. ? : 의 우선순위가 =보다 높기 때문에 =보다 먼저 계산되기 때문이다.

<날개셋> 한글 입력기로 복잡한 수식을 다뤄 본 분들은 이미 아시겠지만, 이 프로그램은 사용자가 입력한 수식을 어느 정도 자동으로 간소화를 한다. 상수 연산은 미리 계산을 해 버리며, 100/0나 2=A 같은 뻔한 에러는 미리 지적해 준다. 그리고 우선순위 규정상 굳이 칠 필요가 없는 괄호도 알아서 제거를 해 버린다.

(A+B)-C는 A+B-C로 바뀌며, 이와 비슷한 맥락으로 (조건 ? A:B)=100도 그냥 조건 ? A:B=100으로 바꾼다. 이건 프로그램의 오동작이 아니므로 놀라지 말고 수식을 사용하면 된다.

그런데 비주얼 C++ 같은 요즘의 C/C++ 컴파일러들은 ? :를 본인이 생각한 것처럼 취급하지 않는 것 같다.
A==100 ?B:C=400 라고 하면 =400은 3항의 C에만 붙지 B에는 붙지 않는다. (A==100 ? B:C)=400이라고 해 줘야 한다.
또한 ?와 : 사이에 있는 2항은 사이에 대입이나 콤마 같은 연산자(우선순위가 ? :보다 한참 더 낮은!)가 괄호 없이 연결되어 있어도 알아서 2항의 일부라고 인식해 주는 듯.
물론, 그렇다고 해서 A=조건 ? 2항: 3항 같은 문장이 있으면 A=까지 조건으로 끌어들이지는 않는다.

이런 세세한 동작 방식에 대해서 정보를 얻고 싶어서 비주얼 C++ 도움말을 찾아봐도, ? :는 대입 연산자보다 우선순위가 높다던가, 2항과 3항의 타입이 서로 다를 때 연산자 값이 정해지는 원칙 같은 원론적인 말밖에 없다. 그 말대로라면 무조건 내 프로그램처럼 괄호를 써야만 할 텐데 말이다.

그 간단한 ? : 연산자에도 의외로 복잡한 사연이 있다는 걸 알 수 있다.
어쨌든 내 프로그램은 ? : 안에 대입이나 콤마 연산을 포함시키려면 무조건 괄호를 써야만 하는 구조가 앞으로도 유지될 것이다.

Posted by 사무엘

2011/06/05 19:20 2011/06/05 19:20
, ,
Response
No Trackback , 4 Comments
RSS :
http://moogi.new21.org/tc/rss/response/521

파이썬 언어

요즘 개인적으로 파이썬을 틈틈이 공부하고 있는데, 나름 재미있다. 대략 20세기 말쯤에 우리나라에 파이썬이 얼리어답터 선구자들에 의해 처음으로 대대적으로 소개됐을 때는, Python의 한글 표기조차도 통일이 안 돼 있었다고 하니 참으로 격세지감이다. 본인은 처음부터 일관되게 파이썬이라고만 들었다.

파이썬이라는 언어가 있다는 걸 본인이 안 건 굉장히 오래 됐다. 거의 2001~2002년 사이인데, 당시 세벌식 사랑 모임에서 '컴바치'라는 필명을 쓰던 송 시중 님과 얘기를 나누다가 파이썬에 대해 처음으로 들었다. 이분, 연락이 끊어진 지는 굉장히 오래 됐는데, 지금은 뭘 하고 계시는지 모르겠다.

그 후 본인은 학교 후배로부터도 파이썬을 좀 공부하는 게 어떻냐는 권유를 몇 차례 받았다. 하지만 오로지 C++ 만능주의에 <날개셋> 한글 입력기 개발에만 정신이 팔려 있던 본인은, “난 비주얼 C++만 있으면 컴퓨터를 내가 원하는 대로 얼마든지 부려 쓸 수 있는데, 그거 또 배워서 뭐 함?” 식으로 별 흥미를 느끼지 못했다. 난 전산학 전공자치고는 컴퓨터 다루는 형태가 아주 기괴하다. -_-;;

그로부터도 또 수 년이 지나고, 무려 대학원에 가서야 본인은 드디어 파이썬을 다시 대면하게 됐다. 파이썬이 말뭉치 같은 대용량 텍스트 데이터를 다루는 도구로서, 전산 비전공자도 쉽게 배울 수 있는 언어로 즐겨 쓰이고 있었던 것이다.

나는 문과 기반이 부족하니 그런 걸 주변 선배들로부터 보충받고, 반대로 전산학 기반이 아주 탄탄하기 때문에 그런 걸 전수해 주는 쪽으로 협업 구도가 자연스럽게 형성되었다. 파이썬 좀 가르쳐 달라는 요청이 있기도 했으니, 본인은 남을 가르치기 위해서 내 자신부터 파이썬을 공부하게 됐다.

한동안 공부해 본 소감은... 파이썬은 꽤 재미있는 언어이다!
type이 runtime 때 동적으로 결정되고 무척 유동적이라는 것은 C++ 특유의 그 경직된 사고방식으로부터 해방감을 느끼게 해 줬다.

{ } 일색인 C/C++, 자바, C# 같은 언어하고만 놀다가...
들여쓰기가 필수 조건이고 for/while/def :로 끝난다는 언어를 접하니 느낌이 새롭다. 좀 베이직과 비슷하다는 생각도 든다. 물론 그렇다고 행번호+GOTO 스파게티 같은 건 전혀 없지만.

다중 대입 기능이라든가 리스트의 slicing 연산은 무척 편리하고 좋았다.
여타 언어였다면 또 임시 변수를 동원한다거나, 번거로운 개체 생성과 반복문이 필요했을 것이다.
C/C++, 자바, C#의 for문은 while문을 형태만 바꾼 것과 완전히 동치이지만, 파이썬의 for 문은 철저하게 복합 자료형의 각 원소를 순회하는 기능에 맞춰져 있다. for문의 설계 철학은 C스타일 언어와 베이직/파스칼 스타일 언어, 그리고 파이썬도 살짝 차이가 있는 것 같다.

언어와 내 사고방식이 완전히 일심동체가 되기 위해서는,
- 리스트 같은 복합 자료형이 내부적으로 구현되는 실제 자료 구조는 무엇이며 시간 복잡도가 얼마나 되는가? 메모리 재할당 비용이 얼마나 되는가?
- 대용량의 복합 자료형을 만들어서 복제하거나 함수 인자로 전달했을 때 shallow copy가 일어나는가, deep copy가 일어나는가?

이런 식의 디테일을 알 필요가 있다.
이것도 몇 번 튜토리얼을 읽고 예제 코드를 짜면서 시행 착오를 겪어 보니 그리 어렵지 않게 이해가 됐다.
문자열과 튜플은 새로운 값의 생성과 대입/재대입만 가능하지, 이미 만들어진 값의 변경은 허용되지 않는다는 대목에서 '아하~!' 소리가 절로 나왔다.
뭐, 문자열도 필요한 경우엔 mutable array 형태로 내부 조작을 할 수도 있다.

파이썬으로 윈도우 API도 호출하고 온갖 희한한 라이브러리를 동원해서 각종 컴퓨터 자동화 작업을 수행하고 별 걸 다 하는 친구도 있는데, 본인은 그 정도 수준은 안 된다. 그래도 이 정도만으로도 좋은 경험이다.

내게 파이썬을 권하던 후배 녀석이 이제는 HTML 공부도 좀 하라고 권한다. 이제는 플래시나 ActiveX 없이도 웹 표준 자체만으로도 별 걸 다 만드니, 훅킹을 한다거나 컴퓨터의 임의의 파일이나 레지스트리를 건드려야 하지 않는 이상 ActiveX의 필요성은 갈수록 없어지고 있다. 웹이 처음에는 그림+글+하이퍼텍스트로 된 문서일 뿐이었는데 지금은 그 자체가 거의 플랫폼처럼 됐다.

Posted by 사무엘

2011/05/25 08:18 2011/05/25 08:18
,
Response
No Trackback , 9 Comments
RSS :
http://moogi.new21.org/tc/rss/response/516

컴퓨터는 배열로 표현된 직사각형 형태의 데이터를 처리하는 걸 좋아하며, 이는 그래픽에서도 예외가 아니다.
그러나 사람이 생각하는 개념을 그래픽 개체의 형태로 표현하다 보면 직사각형이 아닌 임의의 모양의 그래픽을 찍어야 할 일이 생긴다.
게임에서는 스프라이트가 좋은 예이고, 굳이 게임이 아니더라도 GUI 환경에서는 아이콘이라든가 심지어 customized 마우스 포인터도 그런 부류에 속하는 그래픽이다.

이런 그래픽은 결국 큰 직사각형 안에서 투명색을 제외한 나머지 색상을 찍는 방법으로 처리하는데, 그 구체적인 테크닉은 역사적으로 아래와 같은 세 양상을 거치며 바뀌어 왔다.

1. 모노크롬이나 그에 준하는 저색상: 비트 연산

그림을 두 장 준비한다. 그리고 그 두 장을 화면에다 그냥 copy만 하는 게 아니라, 화면에 이미 있는 픽셀과 비트 연산을 하여 그 결과를 찍는다. 이것을 raster operation이라고 하는데, 비트 연산은 CPU-friendly한 작업이기 때문에 컴퓨터가 나름 빠르게 수행할 수 있다.

준비해야 하는 그림은,
찍어야 할 내용이 그려져 있고 배경은 '검은색'(0)으로 처리되어 있는 '원래 비트맵'과,
원래 비트맵하고는 정반대로 배경은 무조건 '흰색'(1)이고 내가 차지하는 스프라이트 영역은 '검은색'(0)으로 처리되어 있는 '마스크 비트맵' 이렇게 둘이다. 마스크 비트맵은 1 아니면 0만 있는 모노크롬이다.
(따라서 '원래 비트맵'만으로는 검은색이 배경인지 아니면 스프라이트가 실제로 차지하는 검은색인지 알 수 없다.)

화면에다가는 먼저 마스크 비트맵을 AND 연산으로 그린다. 원래 화면에 있던 픽셀이 X라면, 마스크에서 배경으로 처리된 픽셀은 X AND 1이므로 X가 그대로 남고, 0이면 0이 되어 검은색이 된다.
즉, 마스크 비트맵에 대한 AND 연산은, 스프라이트가 칠해져야 할 영역만 시꺼멓게 만드는 효과를 낸다.

그리고 다음으로 이 자리에다가 원래 비트맵을 XOR 연산으로 그린다.
0 XOR X = X이므로, 이 연산을 수행해 주면 화면이 0으로(특히 마스크 비트맵 AND 연산으로 인해 0이 된) 시꺼먼 곳은 원래 비트맵이 그대로 그려지고, 원래 비트맵이 0인 배경은 아무 변화가 생기지 않는다.

사용자 삽입 이미지

그림의 출처는 위키백과.
이로써 스프라이트가 멋있게 그려졌다.
도스용 게임 중에 <위험한 데이브>는 이런 초보적인 XOR 방식으로 스프라이트를 찍었기 때문에, 검은 배경이 아니라 두 스프라이트가 겹치면 화면에 잔상이 남곤 했다.

옛날 윈도우 9x 시절에.. 컴퓨터 메모리가 많이 부족해서 하드디스크 스와핑/thrashing이 일어나고 프로그램의 각종 아이콘들이 그려지는 게 눈에 보일 때는... 아이콘이 차지하는 영역이 먼저 시꺼매지거나 반대로 잠깐 하얗게 번쩍이는 걸 볼 수 있었다. 흠, 프로토스 건물도 소환이 끝났을 때 실루엣이 허옇게 번쩍이다가 원래 형태가 드러나는데...;; raster 연산을 더블버퍼링 없이 화면에다 바로 그리다 보니, 컴퓨터 속도가 느려졌을 때 그 중간 과정이 눈에 띄는 것이다.

검정에다가 원래 비트맵의 색을 합성할 때는 이론적으로 OR을 써도 되는데 XOR이 의도적으로 쓰이고 있다.
이는 XOR이 유용하기 때문이다. XOR 1은 비트를 반전시켜 준다는 특성상, XOR 연산으로 그린 그림은 거기에다 XOR을 한번 더 해 주면, 다른 곳에 영향을 주지 않고 자기가 차지하고 있던 영역에서만 완전히 지워진다.

XOR 연산은 컴퓨터의 입장에서는 매우 부담이 가볍기 때문에, 마우스 선택 영역을 나타내는 점선 사각형이라든가 창 크기를 조절하는 작대기처럼 수시로 업데이트를 해 줘야 하는 비주얼 효과를 나타낼 때 즐겨 쓰인다.
아니, 텍스트 블록이라든가 깜빡이는 커서(캐럿)조차도 반전 사각형이니까 XOR이다.

마우스 포인터도 XOR 연산이다. 텍스트 입력란을 뜻하는 I자(beam) 모양의 마우스 포인터는 검은색이 아니라 배경색에 대한 반전색이다. 마스크 비트맵 값을 0이 아닌 1로 둬서 배경을 지우지 않은 상태에서 XOR 비트맵도 1로 해 주면 배경색이 반전되는 효과가 난다. ^^;;

XOR 연산은 디지털 컴퓨터가 존재하는 한 그래픽에서 언제까지나 없어지지 않고 쓰일 방식이긴 하지만... 오늘날은 다소 촌스러운(?) 것으로 간주되고 있기도 한다. GPU님이 계시니 화면 비주얼을 굳이 CPU 친화적인 방법만 고집할 필요는 없는 듯. 그래서 요즘은 뭔가 선택 영역을 나타낼 때 알파 블렌딩을 동원하여 다 옅은 파란 배경 + 더블버퍼링으로 대체되는 추세이다. 화면 전체의 DC를 얻어와서 XOR 연산을 시키는 건 Aero 환경에서는 오히려 성능을 더욱 떨어뜨리는 짓이기도 하니 말이다.

2. 모노크롬 이상 16~256색 사이: 컬러 키(color key)

그 후 컴퓨터의 그래픽 카드의 성능이 향상되면서, 256색 시대가 열렸다. 256색은 팔레트 조작이라는 과도기적인 괴악한 개념을 도입한 걸로도 유명하다.
색깔이 적당히 많아졌기 때문에, 비트맵에서 256색 중 하나만 투명색으로 예약하여 쓰지 않고 나머지 색은 그대로 찍게 하는 방식이 유리하다. 마스크 비트맵 따위를 번거롭게 구비할 필요가 없다. 또한 256색은 RGB 값이 아니라 인덱스 기반 컬러를 쓰기 때문에, xor 반전 연산이 어차피 그렇게 큰 의미를 지니지도 않는다. (실제 색깔값이 반전되는 게 아니라 팔레트 인덱스 번호가 반전되기 때문)

256색 전용으로 유명한 gif 그래픽 파일이 이런 컬러 키를 지정하여 투명색을 지정할 수 있다.
윈도우 API에도 비트맵이나 아이콘의 (0, 0) 위치 픽셀을 투명색으로 간주하고 그려 주는 함수가 있으며, SetLayeredWindowAttributes 함수는 컬러 키를 지정하여 해당색을 투명하게 처리함으로써 non-rectangular 윈도우를 만드는 효과를 내어 준다. region을 만들지 않고도 동일한 일을 할 수 있다는 뜻이다.

3. 트루컬러: 알파 채널

투명색 처리의 최종 완전체는 바로 알파 채널이다. 이건 과거의 픽셀 raster operation과는 차원이 다르며, 컴퓨터가 빨라진 정도를 넘어 그래픽 가속을 위한 별도의 GPU까지 등장하면서 가능해진 궁극의 기술이다.
매 픽셀에다가 이분법적인 투명 여부가 아니라, 이 픽셀이 배경과 얼마나 짙게 오버랩될지 반투명 등급 자체가 추가로 들어간다. RGB에 이어 A까지, 가히 색깔의 4차원화인데, 기계 입장에서는 한 픽셀당 딱 정확히 32비트이니 처리하기에는 다행히 좋다.

256색을 초월한 천연색 그래픽에는 워낙 많은 개수의 색상이 쓰이기 때문에.. 그 중 딱 한 색깔에다가만 컬러 키를 부여하는 게 무의미하다. 그리고 마치 글꼴에도 안티앨리어싱을 하듯, 스프라이트도 경계가 배경색과 부드럽게 융합해야 트루컬러의 진정한 의미가 살아난다. 그래서 알파 채널이 필요한 것이다.

윈도우 98에서 알파 채널을 적용한 비트맵 찍기라든가 그러데이션을 한번에 처리하는 API가 처음으로 추가됐다. 프로그램의 제목 표시줄에 그러데이션 효과가 윈도우 98에서 처음 추가되었는데, 바로 이 API를 쓴 것이다.
그리고 윈도우 XP에서는 알파 채널이 적용된 확장 아이콘이 처음으로 도입되었고, GDI+는 그리기 기능에 전반적으로 알파 채널을 염두에 두고 설계되었다. 하지만 GDI의 기본적인 벡터 드로잉 함수는 그런 새로운 기술로부터 소외되어 있으니 안타까울 뿐.

윈도우 비스타는 48*48도 모자라서 아예 256*256 크기의 아이콘을 지원한다. XP 때부터 이제 아이콘 하나가 2~3만 바이트에 달하는 시대가 됐는데(윈도우 3.1 시절에는 1~2천 바이트.. -_-), 전통적인 ico는 bmp와 같은 '무압축 포맷'인지라 256*256 크기의 32비트 픽셀을 저장했다간 크기를 감당할 수가 없기 때문에, ico 포맷은 내부적으로 png 파일도 포함할 수 있게 구조가 확장되었다.
gif를 대체하는 새로운 이미지 포맷인 png는 알파 채널을 지원한다. 그 자그마한 아이콘 하나도 전문 그래픽 디자이너가 포토샵으로 만들어야 하는 시대가 도래한 지 오래이다.

윈도우 내부적으로는 아이콘과 마우스 포인터 파일은 거의 동일한 포맷으로 간주된다. 아이콘은 이미지 이미지 비트맵과 마스크 비트맵 이렇게 둘 들어있는 형태이며, 마우스 커서는 거기에다 센터 위치가 추가되고.. 애니메이션 포인터는 gif스럽게 프레임이 더 추가되겠구나.
알파 채널이 등장하면서 마스크 비트맵은 존재 가치가 상당수 퇴색하긴 했으나, 오늘날에도 고전 테마(XP의 Luna, 비스타의 Aero 따위가 없는)에서 아이콘을 찍을 때라든가 disabled 상태 같은 변형 상태를 찍을 때 참고 정보로 쓰이기 때문에, 완전히 필요가 없어진 것은 아니다.

요컨대 오늘날은 기술 발전의 정도에 따라 최소한 세 가지 형태의 투명색 표현 기법이 쓰이고 있는 셈이다. 흥미로운 사실이다.

Posted by 사무엘

2011/01/24 07:35 2011/01/24 07:35
, , ,
Response
No Trackback , 7 Comments
RSS :
http://moogi.new21.org/tc/rss/response/454

My Programming Life

1.

<날개셋> 한글 입력기는 동일한 입력기 커널을 공유하는 세 개의 프런트 엔드가 있다.
그 중에서 가장 존재감 있는 터줏대감은 전용 에디터인 편집기이고, 실질적으로 가장 널리 이용되는 프로그램은 윈도우용 IME인 외부 모듈이다. 한편, 편집기처럼 실행되어 마치 IME처럼 동작하는 포인팅 장치 입력 유틸리티인 입력 패드도 지난 5.3 버전에서부터 추가되어 제 3의 프런트 엔드 구실을 하고 있다.

그 중 가장 먼저 만들어진 ‘편집기’는... 프로그램을 만든 본인부터가 에디터로서 아주 유용히 사용한다.
차라리 외부 모듈은 디버깅 할 때 외에는 사용하지 않는다. 운영체제의 기본 IME로 지정되어 있으면 파일을 고칠 수가 없어서 디버깅을 못 하기도 하기 때문이다.

<날개셋> 편집기는 어떤 점에서는 아주 답답하다. 가변폭 글꼴이 지원 안 되고 글씨 크기 조절도 안 되고, ClearType 렌더링이라든가 OpenType 스펙 등 오늘날의 모든 최신 타이포그래피 기술로부터 완벽하게 소외된 외딴 섬이기 때문이다.

그러나 한편으로 <날개셋> 편집기는 아주 작고 가벼우면서도 윈도우 95 이래 어떤 OS에서나 동일하게 유니코드 5.2 옛한글을 마음대로 조합할 수 있고 한글을 내 마음대로 다룰 수 있는 우리집 안방 같은 공간이다. 내가 만든 프로그램이어서 자화자찬 차원이 아니라 정말로 그렇다.
입력 기능뿐만 아니라 다양한 텍스트 필터도 있고, 한글을 자모 단위로 찾고 입력기에다 넘겨주는 글쇠를 붙여넣는 것 같은 아기자기한 기능도 있다. 도스 시절 추억의 도깨비 한글 비트맵 글꼴을 볼 수 있는 건 덤이다.

예전에는 옛한글은 오로지 내장 글꼴로밖에 표현할 수 없었는데 5.3에서부터 임의의 조합 테이블과 추가 자모를 내장 가능한 자체 비트맵 글꼴 포맷을 제정함으로써 <날개셋> 한글 입력기의 커널은 나름대로 글꼴도 독립을 이뤘다. 아래아한글 1.x와 비슷한 글월 입력 환경을 윈도우 환경에서 재현해 낸 것이다.

완전한 텍스트 에디터 엔진을 처음부터 새로 만들었기 때문에, 앞으로 한글 표현 방식이 어떻게 바뀌든 이 구조에 맞춰 엔진을 마음대로 내가 고칠 수 있다.
리눅스나 맥 OS에서는 이런 게 언제쯤 상륙 가능할까? ㄲㄲ

2.

지금까지 <날개셋> 한글 입력기를 만드는 과정에서 그 당시엔 내가 방법을 전혀 몰라서 어려움을 겪던 고비가 몇 차례 있었다.
- 인스톨 패키지 만들기(2002~2003년): MSI 기반으로 완전히 해결
- 외부 모듈(2004~2005년): 3.x 초창기 버전 때 무수한 시행 착오를 겪으면서 결국 안정화 단계. 하지만 “아직까지도” 일부 극소수 몰상식-_-한 응용 프로그램에서 사소한 오동작 버그 신고가 올라오고 있음
- 64비트(2007년): 결국은 본인이 64비트 기계를 직접 장만하면서 지원에 성공.

3.

한 컴퓨터를 놔두고 세벌식 사용자인 본인과 두벌식 사용자인 지인이 같이 앉아 문서를 읽으면서 검토와 교정을 하고 있었다. 이때 복벌식 입력 방식을 아주 유용하게 사용했다. 글자판 전환을 할 필요 없이 서로 자기에게 익숙한 글자판으로 자기가 수정하고 싶은 곳에서 바로 글자를 입력하면 되니 이렇게 편할 수가 없었다. ^^

이거 하니까 세벌식 관련 다른 팁이 또 생각난다. 세벌식 숫자 배열이 익숙한 분이라면, numlock이 켜져 있을 때 오른손 숫자 자리가 non-shift 자리로 내려오게 하면 엑셀 같은 데서 숫자 입력을 아주 편리하게 할 수 있다. <날개셋> 한글 입력기로는 가능하다.

4.

버전 5.53 내지 5.65쯤부터 추가되었지 싶은데, <날개셋> 편집기로 프로그램이 아닌 문서 창(MDI)의 시스템 메뉴를 보면 해당 문서 파일의 ‘속성’ 창을 바로 꺼내거나, 탐색기를 꺼내거나 전체 경로를 복사하는 명령이 있다. ‘파일 경로 복사’를 고르면 되는데, 지금까지는 진짜 말 그대로 파일의 경로가 텍스트 형태로 복사되어 메모장에서만 그걸 붙여넣을 수 있었다.

그런데 탐색기에서 Ctrl+V를 누르면 해당 파일 자체가 실제로 복사도 되게끔 프로그램을 고쳐 봤다. 메모장과 탐색기는 클립보드를 사용하는 방식이 완전히 다르기 때문에 이 기능은 서로 충돌을 일으키지 않으며, 이렇게 하니까 아주 편하다. 5.8 버전에 이 기능이 반영되지 못해서 아쉽다.

5.8을 릴리즈한 후 현재까지 도움말의 오타 내지 로그인 화면· 아웃룩· vim 등에서의 사소하지만 쉽지 않은 외부 모듈 관련 버그가 몇 개 보고되어 있다. 하지만 다들 프로그램의 성능이나 안정성(죽는다거나-_-)과 관련된 건 아니다. MS IME의 소스를 직접 보지 않는 이상 이런 것까지 다 완벽하게 처리하는 버그 없는 IME란 제작 불가능하다. -_-

5.

다음은 <날개셋> 타자연습 이야기. 지금부터는 그림도 좀 곁들이겠다.

사용자 삽입 이미지
요즘도 실력 유지를 위해 타자 연습을 안 하는 건 아닌데,
주옥같은 연습글을 만들었다. 다음 버전에 추가할지 진지하게 고민 중이다. ^^;;

공 병우 세벌식은 10년을 넘게 써도 한글의 위상을 끌어올린 정말 위대한 발명품임이 느껴진다. 그 반면 저 불편한 현행 두벌식 글자판은 어떻게 쓰는지 그걸로 빨리 치는 사람들이 대단하기 그지없다. 세벌식의 단점--기껏해야 글쇠 수 좀 많고 4단 쓰는 것--에 비해 두벌식의 단점은 훨씬 더 치명적이다.

사용자 삽입 이미지
2008년부터 2010년까지 존재하는 본인의 게임 점수판은 전부 ‘승리’(12단계 깨고 엔딩)이다. 본인이 사무엘이라는 이름을 쓰기 시작한 건 2008년 말부터임.
<날개셋> 타자 게임은 과거의 한메 타자 베네치아보다 훨~씬 더 어렵지만 요즘은 한글 타자가 워낙 일상화했기 때문에 본인 말고도 엔딩 보는 사람이 꽤 있을 것이다.

6.

끝으로, 10년 전에 만들었던 WordTech 엔진(컴퓨터 자동 대국 기능)을 요즘 완전히 새로 다시 짜고 있다. 스크린샷은 기존 WordTech와, 새 엔진(GUI를 갖다붙이지 않은 콘솔 프로그램)끼리 서로 검증 대국을 시키는 모습이다.

사용자 삽입 이미지
본인은 <날개셋> 한글 입력기를 만들기 전엔 국내에서 거의 최초로 크로스워드 게임 엔진을 만든 바 있으나... 그 당시의 작품은 지금의 관점에서 보면 기술적으로 개허접.. ㄲㄲㄲㄲ

요즘은 워낙 컴퓨터가 똑똑해진 덕분에, 굳이 이것보다 더 빠르고 메모리를 덜 쓰는 크로스워드 게임 엔진을 만든다는 게 큰 의미는 없지만... 이번에 새로 짠 코드는 메모리 사용량, 계산량, lexicon의 자료구조와 알고리즘, 코드의 깔끔함과 재사용성 등 모든 면에서 10년 전의 구닥다리 코드와는 비교가 되지 않는다. 참으로 아름답다. ^^;;

사실, 이렇게 만들면 된다는 이론적 기반은 이미 수 년 전에 완성되었지만 <날개셋> 개발 때문에 뒷전으로 밀려서 지금까지 작업을 못 하고 있었을 뿐이다.
WordTech도 버전업 좀 하고 싶은데.. ㅠㅠ 컴퓨터과학과 대학원 수업에서 무슨 과목으로든 프로젝트로 좀 할 기회라도 있었으면 좋겠다. 이 엔진 얹으면 버전 4.0으로 가는 건데.;;

콘솔은 만국의 공통 인터페이스이다 보니(표준 입출력 스트림^^), 엔진을 비주얼 C++뿐만이 아니라 오랜만에 DJGPP로도 컴파일해서 도스에서 돌려 봤다. 똑같이 32비트이기 때문에 별 어려움 없이 돌아간다. 지금도 DJGPP가 버전업이 되고 있는지는 모르겠지만 내가 보유하고 있는 건 무려 1997년에 설치한 버전. 혹시 bool 키워드가 지원되지 않나 확인해 봤는데 다행히 지원한다.

10년 전에는 DJGPP의 그 느린 빌드 속도가 무척 거슬렸으나 지금은 그마저도 전광석화. 별도의 도스박스 같은 에뮬뿐만이 아니라 그냥 윈도우 운영체제의 NTVDM에서도 잘 돌아간다.
단, printf의 포맷 지정자로 %c만 인식하고 %C는 인식하지 않는다. 대문자를 찍는다는 생각에 %X와 %x(16진수 숫자)를 구분하듯 습관적으로 %C를 지정해 줬는데 인식이 안 되더라. 뭐, 어차피 찍을 때 chCode+'A' 식으로 대문자를 지정하기 때문에 %c와 %C는 전혀 구분할 필요가 없고 %c만 지원해도 충분하긴 하다.

이상으로 본인의 programming life 잡설 끗.

Posted by 사무엘

2010/12/29 16:46 2010/12/29 16:46
, , , , , ,
Response
No Trackback , 12 Comments
RSS :
http://moogi.new21.org/tc/rss/response/440

메리 크리스마스 인사부터 하고... ㅋ
본인 블로그의 정기 구독자-_-라면 이미 귀가 따갑게 들으셨겠지만, 본인은 10년 전, 제 17회 한국 정보 올림피아드(KOI) 공모 부문의 고등부 대상 수상자이다. 그리고 얼마 전엔 모듈 음악에 대해 글을 쓰면서, 바로 전 해인 16회 대회의 고등부 금상 수상자에 대해서 언급했었다. 그때는 대상 수상자가 없었다.
이제 이 글에서는 전 회에 이어, 본인이 참가한 해의 바로 이듬해인 18회 대회의 고등부 대상 수상자에 대해서 얘기하도록 하겠다.

그 주인공은 바로 김 성진 씨.
학창 시절부터 일찌감치 경시가 아닌 공모 테크를 타고 뭔가 창의적인 아이템으로 소프트웨어 개발에 매진했다는 점에서는 본인의 진로와 비슷하다. 그리고 지금까지 한 우물만 죽어라고 파고 있다는 점에서도 본인과 공통점이 있다. (무슨 분야인지는 곧 소개하겠다.) 그런 외골수는 나밖에 없는 줄 알았는데 여기 또 있다. ^^

이 친구는 KOI뿐만이 아니라 창의성 대회나 다른 소프트웨어 공모전 등에서 자기의 동일 아이템으로 상을 휩쓸었고, 본인보다 매스컴도 훨씬 더 많이 탔으며 IT 분야 사회 활동을 더 활발히 해 왔다. 사교/사회성, 정치성 자체가 본인과는 비교할 수 없이 더 뛰어난 사람이다.

이 친구의 보유 기술 및 아이템이 뭐냐 하면,
인터넷 보안, 음란 사이트 차단, 자녀 컴퓨터 사용 제어(parental control), 인터넷/게임 중독 예방 쪽이다. 관심 분야부터가 지극히 사회적인 쪽이지 않은가?
그걸 수 년째 연구한 솔루션을 만들어서 그는 18회 KOI에서 당당히 대상을 차지하고, 일반고 출신으로서 지정 대회 우수 입상자로 카이스트에 진학했다. 본인은 그와 2001년에 처음으로 메신저에서 만났고, 이내 학교에서 볼 수 있었다. 굉장히 예의바르고 인상이 좋은 사람이었다는 기억은 아직까지 생생하다.

그 후 2004년 가을에는 전산학과에서 제 1회 KAIST Computing Festival이라는 행사를 열었는데, 그때 대회 참가자로서 또 서로 만날 일이 있었다.
그는 확실히 이론보다는 실무형 인재였고, 내 예상과는 달리 전산학과가 아닌 산업디자인과에 진학해 있었다. 전산/산디 복수 내지 부전공인지는 확실히 잘 모르겠다. 저런 친구야말로 카이스트 전산과의 학부 과목인 ‘컴윤리’는 꼭 들어야 했을 텐데 말이다. (그러고 보니 김 진형 교수님도 불과 몇 년 뒤면 정년이다. 세월 한번 무섭다.)

그는 산디과 소속답게 자기 작품을 소개하는 발표용 프레젠테이션은 정말 기가 막히게 잘 만들었던 걸로 본인의 기억에 남아 있다. HCI(사람-기계 커뮤니케이션) 쪽에도 관심이 많은 듯. 스티브 잡스 근성이라도 있는 걸까? ㄲㄲㄲ

뭐, 사족을 덧붙이자면 그 교내 공모전에서도 본인이 출품한 <날개셋> 한글 입력기 3.02가 1등을 했다.
카이스트 전산학과는 가히 전국에서 날고 기는 수학 덕후· 컴퓨터 괴물들이 우글거리는 곳이고, 난 그 집단 안에서는 별 보잘것없는 중하위권 학부생에 불과했다. 그럼에도 불구하고 거기서까지 내 작품이 최고로 인정받을 수 있었던 것은 1, 2년 연구한 작품이 아니니 짬과 연륜면에서 타 작품들과 비교가 안 되고, 또 한글을 이런 식으로 입력할 수 있다는 게 세벌식 사고방식으로는 당연한 것이지만 두벌식밖에 안 써 본 사람이라면 카이스트 교수에게라도 충분히 창의적이고 참신하게 작용했기 때문인 것 같다.

본인이 국어학하고 양다리를 걸쳤다면, 김 성진 씨는 디자인과 양다리를 걸쳤다. 그는 카이스트에서 산디과 석사까지 마친 후, 아예 (주)휴모션이라고 벤처기업 창업을 했다. 그게 2008년의 일이고, 현재까지 어엿한 사장님이 돼 있다. ^^;; 창업 과정에서 카이스트로부터 지원을 당연히 아주 많이 받았다. 보아하니 회사는 대전의 유성 고속버스 터미널에서 꽤 가까운 곳에 있는 듯.
사장이 디자인 전공이다 보니, 핵심 기술인 ‘컴퓨팅 안전’ 분야 솔루션뿐만이 아니라 웹사이트 내지 심지어 회사 CI의 디자인 용역까지 담당하는 모양이다. 대단한 후배이다. 본인과 나이 차차도 별로 안 난다.

정올 공모 출품작 아이템을 이렇게 사업 아이템으로까지 스스로 발전시켜 잘 나가고 있는 입상자가 주변에 있으니 부럽기도 하고 훈훈하다. 정올 공모에서 이렇게 입상하고 덤으로 카이스트 같은 좋은 면학 환경까지 거쳐 간 인재들이 특별히 전산학하고 다른 분야와의 학제간 연구를 통해 우리나라에 뭔가 좋은 일을 많이 했으면 좋겠다. 본인 역시 그 꿈을 이루려는 의욕이 있어서 뒤늦게나마 협동과정 대학원에 갔다. 나는 그렇게 학구파는 아니지만 저 친구 같은 사교력이나 사업 수완은 더 없기 때문에-_-;; 일단 공부부터 좀 하려고..;;
성진 후배가 이 글을 볼 일이 있을지 모르겠지만, 그의 성공과 사업의 번창을 기원한다.

Posted by 사무엘

2010/12/24 18:27 2010/12/24 18:27
, , , ,
Response
No Trackback , 2 Comments
RSS :
http://moogi.new21.org/tc/rss/response/437

우리는 C/C++ 언어에 대해 배울 때, 이 언어는 근본적으로 컴파일과 링크를 거쳐 결과물이 만들어지며, 이 과정에서 소스 코드가 obj 파일로 바뀐다는 말을 듣는다. 그런데 이런 중간 파일들의 내부 구조는 어떨지, 최종 결과물인 실행 파일의 형태와 중간 파일 사이의 관계는 어떨지 등에 대해서 궁금하게 생각해 본 적은 없는가?

물론 obj 파일에는 컴파일된 기계어 코드가 잔뜩 들어있을 것이고 lib는 그냥 이미 컴파일된 obj 파일의 컬렉션에 불과하다. 하지만 그걸 감싸는 컨테이너 포맷 자체는 필요할 것이다.
C++의 경우, 함수의 이름을 prototype대로 decorate하는 방식이 표준으로 제정된 적이 없어서 그 방식이 컴파일러마다 제각각인 것으로 악명 높다. 그렇다면 이런 obj, lib 파일 포맷도 언어마다, 혹은 컴파일러마다 제각각인 것일까?

결론부터 말하자면, 정답은 ‘No’이다. obj, lib 같은 파일 포맷은 실행 파일의 포맷과 더불어 굉장히 시스템스러운 포맷이고, 일반적인 응용 프로그램의 개발자가 거의 관심을 가질 필요가 없는 분야임이 틀림없다. 컴파일러를 만든다거나, 골수 해커 같은 부류가 아니라면 말이다.

이런 건 그렇게까지 다양한 파일 포맷이 존재하지 않으며, 다양하게 만들 필요도 없다.
인텔 x86 기계에서는 전통적으로 인텔 사가 고안한 OMF(object module format이라는 아주 평이한 단어의 이니셜) 방식의 obj/lib 포맷이 독자적으로 쓰였다. 굉장히 역사가 긴 포맷이며, 볼랜드, 왓콤, MS 등의 컴파일러에서 다 호환됐기 때문에 서로 다른 컴파일러나 언어로 만든 obj 파일끼리도 이론적으로는 상호 링크가 가능했다. 물론, 언어별로, 특히 C++의 경우 아까 언급했듯이 decoration 방식이 다르면 명칭이 일치하지 않아 혼용이 곤란하겠지만, 이건 파일 포맷 자체의 문제는 아니었다.

그런데, 32비트 시대가 도래하면서 사정이 약간 달라졌다.
machine word의 크기가 커지고 CPU의 레지스터 구조도 달라지고.. 그에 따라 obj/lib 파일의 포맷도 일부 필드의 크기가 확장되는 등 변화를 겪게 되었으며, 인텔 사에서는 OMF 포맷을 32비트로 확장한 업그레이드 버전을 내놓았다. 마치 지금 윈도우의 PE 실행 파일도 64비트에서는 기본적인 뼈대는 그대로 유지하되, 규격이 확장된 것과 같은 이치이다.

컴파일러들은 대체로 그 규격을 따르기 시작했으나, 이때 MS에서는 꽤 과감한 결정을 내렸다.
기왕 32비트로 갈아타는 김에, 자기네가 만드는(OS/2의 밑천으로? ㄲㄲ) 순수 32비트 운영체제인 윈도우 NT에서는 공식 사용하는 실행 파일과 obj/lib 파일의 포맷을 싹 바꾼 것이다.
어디 그뿐일까? 메모리가 귀하던 1990년대에 그때 이미 유니코드를 고려하여 딱 16비트 wide string을 내부 자료 구조로 채택했다. 본인이 보기에 윈도우 NT는 출발이 굉장히 대인배스러웠다.

새로운 포맷은 단순히 구조체 필드만 32비트에 맞게 키운 게 아니라, 더 보편적인 이식성과 확장성을 고려해서 설계되었다. 코드, 데이터 등 용도별로 다양한 chunk를 둘 수 있고, CPU 정보도 넣어서 굳이 x86뿐만이 아니라 어느 플랫폼 코드의 컨테이너로도 활용할 수 있게 했다. 또한 어차피 똑같은 기계어 코드가 들어있는 파일인데 obj/lib/exe 사이의 구조적 이질감을 낮춰서 일단 컴파일된 코드의 링크 작업을 더욱 수월하게 할 수 있게 했다.

그래서 MS는 32비트 컴파일러에서는 AT&T가 개발한 COFF(Common Object File Format) 방식을 약간 변형한 obj/lib를 사용하기 시작했고, 32비트 실행 파일은 잘 알다시피 COFF의 연장선에 가까운 PE(Portable Executable) 방식을 채택했다. 이 컨벤션이 오늘날의 64비트에까지 고스란히 전해 내려오는 중이다.

그렇게 MS는 과거 유물을 미련 없이 내버렸지만, 볼랜드 컴파일러는 32비트 윈도우용도 여전히 OMF 방식을 사용했고, 왓콤처럼 당시 16비트/32비트 도스/윈도우를 모두 지원하던 컴파일러는 OMF와 COFF 방식을 혼용까지 해서 당시 개발자들에게 상당한 혼란을 끼쳤다고 한다. 윈도우 운영체제가 16비트에서 32비트로 넘어가면서 이런 것까지도 정말 넘사벽에 가깝게 세상이 바뀐 것이다. 참고로 DJGPP는 도스용 컴파일러이지만 32비트 기반이고 COFF 방식 파일을 사용한다.

1985년에 나온 윈도우 1.0 이래로 16비트 윈도우가 사용하던 NE 포맷은 chunk 같은 게 없었다. 정보 자체를 식별하는 방법이 없이 요 정보 다음엔 무슨 정보, 다음에는 무슨 정보.. 딱 용도가 고정되어 있었고, 뭔가 확장을 할 수가 없었다. 상당히 원시적인 포맷이었다는 뜻. 개인적으로 그 시절에는 컴파일과 링크가 어떻게 이뤄졌고 DLL import/export가 어떤 방식으로 되었는지 무척 궁금하다.

또 생각나는 게 있는데, 과거에 똑같은 베이직 컴파일러이지만 MS가 개발한 퀵베이직은 굉장히 C언어에 가깝고, 파워베이직은 파스칼에 가까운 빌드 모델을 사용했다. 전자의 경우 헤더 파일을 인클루드하고 소스 파일을 obj로 컴파일하고, 각종 라이브러리와 링크하고... C와 똑같지 않은지? obj/lib 파일 포맷은 당연히 인텔 OMF 방식이었다.

그 반면, 파워베이직은 파스칼처럼 unit이라는 패키지를 만들고, 그걸 간단하게 use하는 것만으로 여타 모듈의 루틴을 사용할 수 있었다. 자바, C#, D 같은 요즘 언어들이야 비효율적인 인클루드(text parsing이 필요!) 방식이 아닌 패키지 import를 선호하는 추세이지만, 그 당시 파워베이직을 개발한 Bob Zale은 분명 파스칼 언어에서 이 아이디어를 따 왔을 것 같다. 물론 그렇다고 해서 파워베이직도 기존 obj 파일과 링크하는 방식이 없는 건 아니었다.
Bob Zale과, 터보 파스칼을 개발한 필리페 칸과는 어떤 사이일지 궁금하다.

C/C++에 전처리기가 있다면, 베이직이나 파스칼 같은 언어는 주석 안에다가 메타커맨드를 넣는 방식을 써 온 것도 흥미로운 점.
아울러, tpu, pbu 같은 저런 unit 파일은 분명 컴파일된 기계어 코드가 들어있는 라이브러리에 가깝지만, 당연히 컴파일러 vendor마다 파일 포맷이 제각각이다. 마치 퀵베이직의 QLB(퀵라이브러리) 파일이 아주 독자적이고 특이한 실행 파일인 것처럼 말이다.

Posted by 사무엘

2010/11/16 10:29 2010/11/16 10:29
, , , , , ,
Response
No Trackback , No Comment
RSS :
http://moogi.new21.org/tc/rss/response/412

직업병

1.
레크리에이션 시간에 하는 OX 퀴즈 말이다. 이거 완전 퀵 정렬스럽다는 느낌이 들지 않는지?
퀴즈는 PIVOT값이다. 정말 알쏭달쏭해서 사람들이 O와 X로 반반씩 갈려야 좋은 문제이고,
너무 쉽거나 해서 사람들이 한데 쏠리면 그건 난감하다. 퀵 정렬도 완전 똑같다. ㅋㅋ

2.
수업 시간에 각 학생들에게 설문지를 돌리거나 과제물을 나눠 준다. 내게도 서류 뭉치가 왔는데, 이게 어디서 왔는지 모르겠고 내가 살펴본 뒤에 다음으로 이걸 어느 방향에다가 넘겨줘야 할지 잘 모를 때가 있다.
이건 C++로 치면 iterator이다. 서류 뭉치는 모든 학생들을 한 번씩 순회하는데, ++itor; 명령이 수행되려면 지금의 순회 위치로부터 다음 순회 위치를 알 수 있어야 한다. 트리 구조를 순회한다면, 각 노드마다 부모 노드 포인터도 갖추고 있어야 한다는 뜻.

3.
요즘 존재하는 수많은 웹사이트들 중, html 수작업으로 만들어진 것들은 로컬 환경으로 치면 기계어로 짠 네이티브 프로그램이고, 블로그 엔진 기반은 닷넷처럼 일종의 상부 계층 위에서 돌아가는 프로그램에다 비유할 수 있을 것 같다.
개인 사용자가 나모 같은 에디터로 홈페이지를 만들 일이 없어졌다는 건, 윈도우 환경에서 어셈블러 수작업으로 프로그램을 만들 일이 없어진 것과 비슷한 맥락이 아닌가 싶다.
하지만 Win32 API 같은 네이티브 계층 자체가 완전히 없어지는 날은 과연 올까?

4.
외솔관에 있는 대학원생 독서실에 있다가 위당관으로 수업을 들으러 간다. 두 건물의 뒤쪽엔 높은 언덕이 있기 때문에 3층과 4층이 뒷문으로 연결되어 있으며, 이 경로를 이용하면 건물 사이를 왕래할 때 번거롭게 1층까지 내려갔다가 다시 올라갈 필요가 없다.
바깥에 비해 상대적으로 어두운 건물 복도를 걸으면서 지하철 터널을 떠올리는 것은 어렵지 않다. 그러다가 잠시 밖으로 나가면, 지하철이 강을 건너거나 서울 지하철 8호선의 복정-산성 구간 같은 곳을 지나느라 잠시 지상으로 나온 것 같은 느낌이 든다.

5.
교회에서 성가대 연습을 한다. 노래를 부르는데 반주자가 악보를 넘기느라 잠시 피아노 반주가 중단되었다. 그래도 노래는 박자나 음정의 어긋남이 없이 계속 잘 이어진다.
이것은 절연 구간을 지나느라 전동차에 전원 공급이 잠시 중단되더라도 차가 관성으로 계속 달리는 것과 같은 맥락으로 풀이할 수 있다. 아울러, 바닷물과 민물을 넘나드는 연어는 교류-직류 겸용 전동차의 예표이다.
일상생활 속에서 철도 패턴을 찾기는 어려운 일이 아니다.

6.
대학 학부까지만 학업을 마치고 취업을 한 건, 지금 생각해 보니 학업이라는 지하철이 서울 시계까지만 건설된 뒤 노선이 끊어졌던 듯한 느낌이다. 학부를 졸업한 지 5년이 지나서야 대학원에 들어가니, 그 선로를 이어서 장거리 광역전철을 건설하는 것 같다.

7.
<날개셋> 한글 입력기 5.65를 공개한 후, 소스를 대대적으로 뒤집어엎었다.
null-terminate 스트링의 write 버퍼를 받는 모든 함수에는 버퍼의 크기에 대한 정보를 추가하고, sprintf 같은 함수 호출도 버퍼 오버런을 일으키지 않게 다 손질했다.
파일을 읽고 쓰는 과정에서 에러 처리를 더욱 강화하고, 범용적인 dll 모듈은 thread-safe하도록 고쳤다.
좀 비효율적이고 불합리하게 만들어져 있던 라이브러리 API를 뜯어고쳤다.

그래서 다음 버전으로 잠정 계획 중인 <날개셋> 한글 입력기 5.8은 5.5 시절부터 비교적 잘 유지되어 왔던 API 하위 호환성이 모두 깨질 예정이다.
타자연습도 덩달아 버전업된다. 입력기에 적용된 프로그래밍 테크닉이 그대로 적용되고, 그리고 연습글을 좀 정리할 생각이다.

6만 줄에 달하는 <날개셋> 한글 입력기 소스 코드를 들여다보노라면 정말 나만의 세계, 나만의 건축물, 나만의 철도 노선에 들어온 느낌이다. 의존도라고는 Win32 API와 몇몇 Ansi C 함수밖에 없으며, 나머지 코드들은 100% 자체 제작이다. 다른 프레임워크나 오픈소스 작품 같은 거 쓴 것이 전혀 없다.

누구에게 돈이나 시간 면에서 단 한 치도 얽매인 게 없이, 전적으로 개인 취미 생활로 개발하는 것이다 보니,
단순히 기능만 되게 하는 게 아니라 소스 코드의 질에도 굉장히 신경을 쓴다.
비록 한 줄에 100칼럼을 꽉꽉 채우느라 겉보기로는 코드가 좀 지저분해 보여도, 구조는 의외로 깔끔한 편. ㅋㅋㅋㅋ

코드에 무슨 공통된 패턴이 반복되는 게 발견되면 함수로 따로 떼낸다거나, 모듈 간의 공통된 기능을 한 기반 클래스로 빼낸다거나.. 이런 식으로 "리팩터링"을 수시로 진행한다는 뜻이다.
이런 거 공사 하나 잘 해서 추상적인 클래스가 하나 탄생하고 상속 계층이 한 단계 올라간다거나 하면,
어려운 버그를 잡은 것만큼이나 기쁘다.

Posted by 사무엘

2010/10/17 18:08 2010/10/17 18:08
, ,
Response
No Trackback , 4 Comments
RSS :
http://moogi.new21.org/tc/rss/response/393

« Previous : 1 : ... 18 : 19 : 20 : 21 : 22 : 23 : Next »

블로그 이미지

그런즉 이제 애호박, 단호박, 늙은호박 이 셋은 항상 있으나, 그 중에 제일은 늙은호박이니라.

- 사무엘

Archives

Authors

  1. 사무엘

Calendar

«   2024/04   »
  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30        

Site Stats

Total hits:
2673644
Today:
336
Yesterday:
1540