|
;;; 변수 index의 값 변화와 Watch Window의 표시 변화를 ;;; 알아보기 위한 예제 (defun test () (setq index 0) (repeat 100 (repeat 1000 ; 시간을 지연시키기 위한 루틴 (princ) ; ) (setq index (1+ index)) ;변수값을 증가 시킨다 (princ "index = ") ;콘솔에 문자열을 출력한다. (princ index) ;콘솔에 변수값을 출력한다. (princ "\n") ;줄을 바꾼다. ) ) |
1. New File 버튼( )을 눌러 새로운 문서 편집창을 연다.
2. <리스트 1>을 1에서 만든 편집창에 입력한다. 세미콜론(;) 뒤는 설명이니 입력하지 않아도 된다.
3. Load Active Edit Window( ) 버튼을 눌러 메모리에 로드 시킨다.
4. index라는 단어를 더블클릭하여 구역으로 설정한다.
5. Add Watch 버튼( )을 눌러 Watch Window에 index 변수를 추가시킨다.
6. 메뉴바에서 Debug 메뉴를 눌러 Stop Once나 Animate 옵션이 선택되어 있지 않음을 확인한다.
7. 콘솔(System Consol)에 '(test)'를 입력하여 프로그램을 실행한다.
8. 콘솔과 Watch Window의 index 변수값이 변하는 것을 본다.
위 과정을 수행하면 콘솔에 나타나는 변화와 Watch Window에 나타나는 변화가 다르다는 것을 알 수 있을 것이다. Watch Window는 변수의 값을 바뀌는 즉시 추적하여 보여주는 것이 아니기 때문이다. 그럼 Animate 모드로 프로그램을 실행했을 때는 어떤지를 알아보자.
1. <리스트 1>시간을 지연시키기 위한 부분 즉
(repeat 1000 ; 시간을 지연시키기 위한 루틴
(princ) ;
)
이 세 줄을 선택한다.(마우스로 드래그를 하거나 Shift키를 누르고 화살표키로 이동한다)
2. Comment Block 버튼( )을 눌러 선택된 문장을 설명(comment)으로 인식시킨다.
3. Load Active Edit Window( ) 버튼을 눌러 메모리에 다시 로드 시킨다.
4. 메뉴바에서 Debug 메뉴를 누르고 Animate 옵션이 선택해 체크 표시가 생기게 한다.
5. 콘솔(System Consol)에 '(test)'를 입력하여 프로그램을 실행한다.
6. 콘솔과 Watch Window의 index 변수값이 변하는 것을 본다.
위 과정에서 1번을 하지 않으면 짜증날 정도로 프로그램의 수행이 늦다. 만약 1번을 하지 않고 Animate 모드로 프로그램을 실행시켰다면 메뉴에서 Debug - Abort Evaluation 항목을 선택하여 프로그램을 중지시키고 다시 시간지연부분을 설명으로 바꾸는 것이 좋을 것이다.
이제 Watch Window의 내용과 콘솔에 표시되는 실재 index 변수값이 동시에 바뀜을 알 수 있다. 프로그램의 실행중에 움직이는
버튼은 Step Indicator라는 것인데 지금 프로그램의 실행이 괄호 안에서 이루어지고 있는지, 아니면 다음 괄호로 들어갈 준비를 하고 있는지를 알려주는 역할을 한다. 그럼 이제 Breakpoint를 이용하여 프로그램을 실행시키자.
1. (princ "\n") 라는 문장의 앞에 커서를 두고 Toggle Breakpoint( ) 버튼을 누른다. 그러면 괄호 앞에 붉은색 사각형이 생긴다.
2. 메뉴바에서 Debug 메뉴를 눌러 Stop Once나 Animate 옵션이 선택되어 있지 않게 한다.
3. 콘솔(System Consol)에 '(test)'를 입력하여 프로그램을 실행한다.
4. 프로그램이 멈추면 콘솔과 Watch Window의 값이 같은지 확인한다.
5. Continue 버튼( )을 눌러 프로그램을 계속시킨다.
6. 다시 4, 5번 과정을 반복한다.
이번에도 역시 변수값이 변하는 것이 Watch Window에 잘 난다.
Breakpoint를 이용한 방법에서는 필요한 곳에서만 멈춘다는 장점이 있다. 또, 멈추어 있는 동안 프로그램을 약간 수정하여 계속 실행할 수도 있고, 추적이 필요한 변수를 Watch Window에 포함시킬 수도 있다.
여기서 Inspect의 맛을 약간만 보고 넘어가자. Watch window의 index 항목을 더블클릭하면 Inspect Window가 나온다. 이 Inspect window를 통해서 현재 변수의 형이 무엇인지. 즉, 정수인지, 실수인지, 문자인지 등을 판단할 수 있고 만약 정수형이라면 다른 진법이나 문자열로 바꿨을 때는 어떻게 되는지 알아볼 수 있다.
Inspect를 이용한 변수의 정밀관찰
Inspect라는 단어는 정밀하게 관찰한다는 의미를 지닌 단어다. 명사형으로 했을 때는 형사라는 의미도 된다. 때문에 이 Inspect 기능의 아이콘은 이다. Inspect 기능을 이용하면 변수의 형이 무엇인지, 복합 변수(리스트나 문자열 등)의 경우 내부로 들어가면 어떻게 되는지 따위를 알 수 있다. Inspect 아이콘은 View 툴바에 있다.
Inspect는 전 절에서 설명했듯이 Watch Window에서 띄울 수도 있지만, Watch Window를 띄울 때처럼 변수명 선택 후 Inspect 버튼( )을 눌러 바로 변수의 내용을 알아보거나 선택 없이 Inspect 버튼을 누르고 변수명을 입력하면 된다. 또, Inspect는 Watch와는 달리 변수만 가능한 것이 아니라 상수(常數)와 AutoCAD 내부에 정의된 테이블(레이어, 블럭, UCS 등)까지도 가능하다.
<그림 10>에서 <그림 14>는 여러 형의 값들을 Inspect한 결과다. Inspect Window의 가장 위의 제목표시줄에는 Inspect된 변수 또는 상수의 형이 표시된다.
정수의 경우는 <그림 10>처럼 2진수(bin), 8진수(oct), 십진수(dec), 16진수(hex), 문자형(char) 등 여러 가지 표시방법으로 보여준다.
실수의 경우 어떤 하위요소도 없다. 실수형은 단순하게 우리가 많이 쓰는 형식으로 숫자를 표시해(예를 들면 1.0E4라는 과학적 표기법으로 된 실수는 10000.0으로 바뀌어 표시된다)준다.
심벌(Symbol)이라는 것은 어떤 데이터를 참조하기 위한 대표명으로 쓰이는 것 즉, 변수명이나 함수명 등을 말한다. 심벌의 Inspect에는 이름, 값, 플래그(Flag) 등이 나타난다. 플래그의 종류와 의미는 다음과 같다. 플래그는 다음절에 설명하게 될 Symbol Service에 의해서 생긴다.
Pa |
Protect Assign |
재정의 방지된 심벌 |
Tr |
Trace |
추적되고 있음 |
De |
Debug on Entity |
디버깅이 이루이지는 요소 |
Ea |
Export to Acad |
보통 AutoLisp 변수로 전환됨 |
리스트형의 경우는 리스트의 내용을 표시하고 그 아래 하나의 창이 더 생겨 리스트 안의 내용들을 하나씩 보여준다. 아래의 리스트에 있는 값들을 더블클릭하면 다시 하나의 Inspect Window가 더 떠서 그 내용을 다시 Inspect해 준다.
파일 Inspect에서 Name은 파일의 이름, Mode는 파일이 읽기모드로 열렸는지, 쓰기 모드로 열렸는지를 나타내고, Id는 파일의 구분자(Identifier)를, Point는 파일 내에서 현재 읽기 쓰기 되고 있는 위치를 나타낸다.
문자열의 경우 문자열이 한자씩 분해되어 나타난다. 이때 화면상에는 똑같은 공백으로 보이는 것도 탭이나 특수문자 등으로 구분되어 보임으로 편리하다.
LISP의 내장함수들은 Subroutine으로 처리되어 이름과 내부적인 번호가 나온다.
사용자가 만든 함수들의 경우 User Subroutine으로 처리되어 이름과 받아들이는 요소들(Parameters), 내부에서 쓰이는 변수(Auxiliary)등을 보여준다.
ADS, ARX, VBA등 LISP 외의 다른 API에서 만들어진 함수는 External Subroutine으로 처리된다.
Visual LISP의 Inspect 기능은 이런 에디터나 변수에 정의된 것 들 뿐만이 아니라 AutoCAD의 데이터베이스에 들어있는 모든 것들을 관찰할 수 있게 해준다. View 메뉴의 Browse Drawing Database 밑의 항목들을 선택하면 그 내용들을 볼 수 있다.
<그림 19>는 View 메뉴의 Browse Drawing Database - Browse All Entities를 선택했을 때 나온 Inspect다. 말 그대로 도면 내에 존재하는 모든 눈에 보이는 요소(Entity)들의 리스트가 나온다.
Browse Tables를 선택하면 <그림 20>처럼 AutoCAD에 있는 테이블(Table)들의 종류가 나오고, 이 중 자세히 보기를 원하는 요소를 더블클릭하면 <그림 21>, <그림 22>처럼 하위요소들이 자세하게 나온다.
Browse Blocks를 선택하면 <그림 23>처럼 도면내에 정의된 블럭들이 나오고, 각 블럭의 이름을 더블클릭하면 <그림 24>처럼 블럭의 자세한 내용이 나온다.
Browse Selections를 선택하면 AutoCAD의 화면에서 내용을 보고싶은 엔티티(Entity)들을 선택할 수 있다. 선택한 엔티티들은 하나의 선택 세트(Selection Set)가 되고 이것이 Inspect된다. 여기서도 자세히 보기 원하는 엔티티의 이름을 더블클릭하면 <그림 26>처럼 자세한 내용이 나온다. <그림 25>와 <그림 26>의 리스트들은 일반적인 Selection Set이나 Entity Name을 가지고 있는 변수를 Inspect 했을 때도 나오는 것은 물론이다.
Inquire Extended Data는 regapp 명령을 통해서 Visual LISP에 등록된 외부 프로그램에서 등록된 추가적인 데이터형들도 Inspect를 할 때 보여줄지를 묻는 것이다. 여기서 선택한 프로그램 내에서 정의된 변수형들은 entget 함수나 Inspect 기능을 통해서 리스트를 보았을 때 나오게 된다.
Symbol Service
앞의 Inspect 절에서 보았듯이 AutoLISP이 사용하는 변수의 종류는 상당히 다양하다. 그러나 이 다양한 형(Type)을 갖는 변수들은 모두 그 나름대로의 이름(Symbol)을 가지고 그 이름을 통해서 호출되거나 참조된다는 공통점을 갖는다. 때문에 Symbol을 효과적으로 다루고 추적한다는 것은 디버깅에 있어서 매우 중요한 것이다.
Visual Lisp에서는 Symbol들을 효과적으로 다루기 위한 Symbol Service를 제공한다. Symbol Service를 이용하기 위해서는 에디터나 콘솔에서 심벌을 선택하고 Symbol Service 버튼( )을 누르면 된다.
Symbol Service Window는 <그림 26>과 같이 툴바(Tool bar), 이름, 값, 플래그(Flag)로 이루어져 있다. 이 Symbol Service Window를 이용하면 모든 선택된 Symbol에 대한 거의 모든 조절과 추적, 디버깅을 수행 할 수 있다.
툴바의 Add Watch 버튼( )과 Inspect 버튼( )의 의미는 쉽게 알 수 있을 것이고, Show Definition 버튼( )은 말 그대로 정의를 보는 버튼인데 아무 심벌에나 쓸 수 있는 것은 아니고 사용자 정의 써브루틴(User Define Subroutine)에만 쓸 수 있다. 사용자 정의 써브루틴이 선택된 경우 이 버튼을 누르면 그 써브루틴이 정의된 곳이 에디터에 나타나고 반전이 되어진다. Help 버튼( )은 심벌에 대한 도움말이다. 이 도움말은 Visual LISP의 내부 심벌에만 적절한 도움말을 보여주고 사용자 정의 심벌들은 정상적인 도움말이 나오지 않는다.
값(Value) 필드에 적절한 값을 써넣음으로써 심벌이 의미하는 것을 바꿀 수도 있다. 물론 내용을 바꾸고 OK 버튼을 눌러야 적용이 된다.
이 Symbol Service에서 가장 중요한 것이 Flag다. 이 플래그들은 전 절의 Symbol의 Inspect에서 약간 살펴보았다. 이제 한 항목씩 자세히 살펴보자.
Trace(Tr) : Tr Flag는 사용자정의 함수(User-define Function)를 추적하는데 쓰인다. Tr Flag가 설정된 심벌은 Trace Window에 호출된 상황과 결과가 출력된다.<그림 27>
Protect Assign (Pa) : Pa 플래그는 심벌의 재정의(Redefine) 되는 것을 방지한다. 만약 Protect Assign Flag가 켜진 심벌을 제정의 하려한다면 에러와 함께 경고가 나오게 된다. <그림 28>은 원주율의 나타내는 상수 PI의 값을 재정의 하려한 경우다. Visual LISP가 기본적으로 제공하는 모든 심벌들과 appreg 함수를 통해서 정의된 모든 심벌은 기본적으로 Pa 플래그가 켜진다. 사용자정의 심벌 중 많이 쓰는 심벌에도 이 Pa 플래그를 설정해주면 에디터에서 마치 내장함수처럼 파란색으로 표시되어 인식하기 쉽게된다.
Debug on Entity (De) : 이 플래그가 설정된 함수들이 참조(Invocation)되면, 마치 그 곳에 Breakpoint가 설정된 것처럼 동작을 하게 된다. 즉, 참조된 함수의 앞에서 프로그램의 실행이 멈춰진다. 주의할 점은 이 플래그의 효력은 함수가 정의(define) 될 때가 아니라 참조될 때 발생한다는 것이다.
Export to ACAD (Ea) : Ea 플래그가 설정된 심벌은 마치 순수한 AutoLISP의 심벌처럼 동작한다. 즉, AutoCAD의 커멘드라인(Command:)에서 함수나 변수를 동작시키거나 변화시키거나 값을 알아낼 수도 있다.
Trace Stack
Visual LISP에는 아주 특별한 디버깅 도구가 있다. 바로 이 Trace Stack이다. 이것은 마치 메모리 구조 중 하나인 Stack처럼 동작한다. 즉, 먼저 들어온 자료가 나중에 나가는 구조를 갖는 임시 저장 장소다. <그림 29>는 스택의 일반적인 동작을 설명하고 있다.
Trace Stack은 Visual LISP에 의해서 '남아있는 해야할 일들을 기억하는 공간'으로 쓰이고 있다. 리습이 괄호로 시작해서 괄호로 끝나는 언어이고, 괄호 안에는 또 다른 괄호들이 들어갈 수 있음을 잘 알고 있을 것이다. Trace Stack은 아직 수행이 끝나지 않은 괄호들이 할 일들을 기억하고 있는 것이다.
Trace Stack의 동작을 이해하기 위해 다음의 예를 입력하고 실행해 보자
(defun stack-tracing (indexVal maxVal) (princ "At the top of the stack-tracing function, indexVal = ") (princ indexVal) (if (< indexVal maxVal) (stack-tracing (1+ indexVal) maxVal) (princ "Reached the maximum depth.") ; 이 줄의 시작에 Breakpoint를 만든다. ) ) (defun c:trace-10-deep () (terpri) (stack-tracing 1 10) ) |
1. New File 버튼( )을 눌러 새로운 문서 편집창을 연다.
2. <리스트 2>을 1에서 만든 편집창에 입력한다. 세미콜론(;) 뒤는 설명이니 입력하지 않아도 된다.
3. 여섯 번째 줄(';이 줄의 시작에 Breakpoint를 만든다'라는 설명이 있는 줄)의 처음에 커서를 두고 Breakpoint 버튼( )을 눌러 Breakpoint를 설정한다.
4. Load Active Edit Window( ) 버튼을 눌러 메모리에 로드 시킨다.
5. Active AutoCAD 버튼( )을 눌러 AutoCAD를 활성화 시킨다.
6. AutoCAD의 커멘드라인에서 trace-10-deep 명령을 실행한다.
7. 다시 Visual LISP으로 돌아온다.
8. Trace 버튼(
)을 눌러 Trace Stack Window를 연다.
어떤 결과가 나오는가? <그림 30>과 같은 결과가 나오면 제대로 실행된 것이다. 내용을 살펴보면 Stack의 제일 위에 Breakpoint가 수행중인 것이 보이고, 그 아래에 계속 재귀호출된 stack-tracing 함수들이 두 개의 인자들과 함께 호출된 것이 보이고, 실행함수, AutoCAD 동작 요구가 있었다는 것, Top-Command 즉 초기상태가 보인다. 사용자는 이 Trace Stack의 내용을 보면서 아직 수행이 끝나지 않은 것들이 무엇이 있는지를 알 수 있는 것이다.
Trace Stack은 앞의 예에서처럼 Breakpoint를 만나서 프로그램이 정지한 경우나 에러가 발생해서 프로그램이 정지한 경우에 그 내용을 볼 수 있다. 정상적으로 프로그램이 수행돼 Top-Level로 돌아간 경우에는 Trace Stack에 아무 것도 남아있지 않다. 또, Trace Stack Window는 자동으로 내용이 갱신되는 것이 아니라 Trace 버튼( )을 눌러주어야 현재의 상태를 볼 수 있게된다.
에러가 난 곳과 원인 알아내기
에러가 일단 발생하면 우리는 거기서 몇 가지 정보를 손쉽게 얻을 수 있다. 그 중 하나가 에러가 일어나게 된 간략한 원인에 관한 메시지고, 다른 하나가 에러가 일어난 위치다.
에러의 간략한 내용은 System Consol에 나타나고 위치는 Last Break 버튼( )을 눌러보면 알 수 있다. 이런 기초적인 내용을 바탕으로 앞에 길게 설명한 여러 가지 기법들을 잘 사용하면 효과적인 디버깅과 함께, 안정적이고 범용적인 프로그램을 작성할 수 있을 것이다.
실행파일 만들기
프로그램을 만들고 완벽한 디버깅이 끝났으면 실행파일로 만들어야 한다. 실행파일로 만든다는 것은 내가 짠 프로그램의 소스(Source)를 남들이 못 보게 한다는 점, 실행 속도가 빠르다는 점 등 많은 장점이 있다. 또한 Visual LISP을 통해 프로그램을 만들면 DCL을 프로그램 안에 내장할 수 있고, 다른 모듈에서 만들어 놓은 프리컴파일(Pre-Compile) 된 프로그램(VLX 파일)도 포함시킬 수 있는 등 여러 가지 장점이 많다.
실행파일을 쉽게 만들기 위해서는 Application Wizard를 이용하면 된다. Application Wizard는 Make 파일을 만들고 여기서 자동으로 실행파일을 만들어 낸다. Application Wizard를 실행시키기 위해서는 File Menu의 Make Application - New Application Wizard를 선택하면 된다.
Application Wizard를 통한 실행파일의 생성은 매우 간단하다. 그냥 지시하는 데로만 따르면 된다. 주의 할 점이 몇가지 있다. 첫번째는 <그림 31>에서 보듯이 Visual LISP이 만들 수 있는 실행파일의 형식이 Standard ARX, ARX with ActiveX automation, VLX의 3가지라는 것이다.
Standard ARX는 일반적이 ARX 프로그램이며, ARX with ActiveX automation은 ActiveX 기능을 이용하는 ARX 프로그램을 만드는 것으로 Standard ARX보다 크기가 더 커진다. 마지막 VLX는 실행파일 자체만 가지고는 동작하지 않고 반드시 Visual LISP이 로드되어 있어야지만 작동이 되는 실행파일이다. VLX로 만들면 크기가 가장 작아지는 장점이 있다.
또 하나 명심해야 할 것은 대부분의 단계에서 그냥 Next 버튼을 누르고 다음으로 가면 문제가 없지만, <그림 32>에 보이는 4번째 단계에는 반드시 포함할 LISP파일이나 VLX 파일, 프로젝트 파일 등을 선택해야 한다는 것이다.. 또, DCL을 포함시키기 위해서는 <그림 33>에 보이는 5번째 단계에서 포함해 주어야 한다.
이런 단계들을 모두 거치고 나면 실행파일이 만들어지는데, 이 실행파일들은 arxload 명령으로 로드할 수 있다.