namespace — 명령과 변수의 컨텍스트 만들기·다루기
namespace — 명령과 변수의 컨텍스트 만들기·다루기
큰 프로그램을 짜다 보면 이름이 서로 충돌하지 않게 코드를 묶어두고 싶어져요. namespace 명령은 명령과 변수를 위한 분리된 컨텍스트를 만들고, 접근하고, 파괴할 수 있게 해 줍니다. 라이브러리 단위로 묶고 필요한 명령만 꺼내 쓰는 Tcl의 조직화 방식이에요.
본문
namespace subcommand ?arg ...?
namespace 명령은 명령과 변수를 위한 분리된 컨텍스트(네임스페이스)를 만들고, 접근하고, 파괴할 수 있게 해 줘요. 네임스페이스에 대한 개요는 아래 "네임스페이스란 무엇인가" 섹션에서 볼 수 있어요. subcommand의 유효한 값은 아래에 나열돼 있어요. 서브명령은 줄여서 쓸 수 있다는 점을 기억하세요.
namespace children ?namespace? ?pattern?
namespace에 속한 모든 자식 네임스페이스의 목록을 돌려줘요. namespace를 지정하지 않으면 현재 네임스페이스의 자식들을 돌려줘요. 이 명령은 이중 콜론(::)으로 시작하는 정규화된 이름을 돌려줘요. 선택적 pattern이 주어지면 glob 스타일 패턴과 일치하는 이름만 돌려줘요. 실제 사용되는 패턴은 다음과 같이 결정돼요: 이중 콜론(::)으로 시작하는 패턴은 그대로 사용되고, 그렇지 않으면 namespace(또는 현재 네임스페이스의 정규화된 이름)가 패턴 앞에 붙어요.
namespace code script
나중에 스크립트 script를 실행하기 위해 현재 네임스페이스 컨텍스트를 캡처해요. script를 namespace inscope 명령으로 감싼 새 스크립트를 돌려줘요. 이 새 스크립트는 두 가지 중요한 속성을 가져요. 첫째, 어떤 네임스페이스에서 평가해도 script가 현재 네임스페이스(namespace code 명령이 호출된 곳)에서 평가되게 해요. 둘째, 결과 스크립트에 추가 인수를 붙일 수 있고 그 인수들이 script에 추가 인수로 전달돼요.
예를 들어 네임스페이스 ::a::b에서 set script [namespace code {foo bar}]가 호출됐다고 해 보죠. 그러면 eval $script [list x y]는 어떤 네임스페이스에서 실행해도(값이 제대로 전달되었다면) ::namespace eval ::a::b {foo bar x y}와 같은 효과를 가져요.
이 명령이 필요한 이유는 Tk 같은 확장이 보통 콜백 스크립트를 전역 네임스페이스에서 실행하기 때문이에요. 스코프된 명령(scoped command)은 명령을 네임스페이스 컨텍스트와 함께 캡처해서 나중에 올바르게 실행될 수 있게 해 줘요. 콜백 스크립트를 만드는 데 이것을 어떻게 쓰는지 예시는 "스코프된 스크립트" 섹션에서 볼 수 있어요.
namespace current
현재 네임스페이스의 정규화된 이름을 돌려줘요. 전역 네임스페이스의 실제 이름은 ""(빈 문자열)이지만, 이 명령은 프로그래머 편의를 위해 전역 네임스페이스에 대해 ::을 돌려줘요.
namespace delete ?namespace namespace ...?
각 namespace가 삭제되고, 그 안에 포함된 모든 변수·프로시저·자식 네임스페이스가 삭제돼요. 프로시저가 네임스페이스 안에서 현재 실행 중이면, 그 프로시저가 반환될 때까지 네임스페이스는 유지되지만, 다른 코드가 이름으로 찾아보지 못하도록 표시돼요. 네임스페이스가 존재하지 않으면 오류를 돌려줘요. 네임스페이스 이름이 없으면 아무 일도 하지 않아요.
namespace ensemble subcommand ?arg ...?
서브명령들의 앙상블(ensemble)로 이루어진 명령을 만들고 다뤄요. 자세한 내용은 아래 "앙상블" 섹션을 참고해요.
namespace eval namespace arg ?arg ...?
namespace라는 네임스페이스를 활성화하고 그 컨텍스트에서 코드를 평가해요. 네임스페이스가 아직 없으면 만들어져요. arg 인수가 둘 이상이면 각 인수를 eval 명령처럼 공백으로 이어 붙여 그 결과를 평가해요.
namespace에 앞쪽 네임스페이스 한정자가 있고 어떤 앞쪽 네임스페이스도 존재하지 않으면, 그것들이 자동으로 만들어져요.
namespace exists namespace
namespace가 현재 컨텍스트에서 유효한 네임스페이스이면 1, 아니면 0을 돌려줘요.
namespace export ?-clear? ?pattern pattern ...?
네임스페이스에서 내보낼 명령을 지정해요. 내보낸 명령은 나중에 namespace import 명령으로 다른 네임스페이스에 가져올 수 있는 명령들이에요. 네임스페이스에 정의된 명령과 이전에 가져온 명령 모두 내보낼 수 있어요. 명령들이 namespace export 명령이 실행될 때 정의되어 있을 필요는 없어요. 각 pattern은 glob 스타일 특수 문자를 포함할 수 있지만, 네임스페이스 한정자는 포함해서는 안 돼요. 즉 패턴은 현재(내보내는) 네임스페이스의 명령만 지정할 수 있어요. 각 패턴은 네임스페이스의 내보내기 패턴 목록에 추가돼요. -clear 플래그가 주어지면 패턴 인수가 추가되기 전에 내보내기 패턴 목록이 빈 상태로 재설정돼요. 패턴이 없고 -clear 플래그가 없으면 현재 내보내기 목록을 돌려줘요.
namespace forget ?pattern pattern ...?
네임스페이스에서 이전에 가져온 명령을 제거해요. 각 pattern은 x, foo::x, a::b::p* 같은 단순하거나 정규화된 이름이에요. 정규화된 이름은 이중 콜론(::)을 포함하고 하나 이상의 네임스페이스 이름으로 이름을 한정해요. 각 "정규화된 패턴"은 내보내는 네임스페이스의 이름으로 한정되며, 정규화된 이름 끝의 명령 이름에 glob 스타일 특수 문자를 가질 수 있어요. glob 문자는 네임스페이스 이름에 나타날 수 없어요. 각 "단순 패턴"에 대해 이 명령은 다른 네임스페이스에서 가져온 현재 네임스페이스의 일치하는 명령을 삭제해요. "정규화된 패턴"에 대해서는 먼저 일치하는 내보낸 명령을 찾고, 그중 어떤 것이 이전에 현재 네임스페이스에 의해 가져와졌는지 확인해요. 그렇다면 대응하는 가져온 명령을 삭제해요. 사실상 namespace import 명령의 동작을 되돌려요.
namespace import ?-force? ?pattern pattern ...?
명령을 네임스페이스로 가져오거나, 네임스페이스에 가져온 명령 집합을 조회해요. 인수가 없으면 현재 네임스페이스에서 다른 네임스페이스로부터 가져온 명령 목록을 돌려줘요. 반환 목록의 명령들은 네임스페이스 한정자가 전혀 없는 단순 이름 형식이에요. 이 형식은 namespace forget와 함께 조합하기에 적합해요(아래 예제 참고).
pattern 인수가 있으면 각 패턴은 foo::x나 a::p* 같은 정규화된 이름이에요. 즉 내보내는 네임스페이스의 이름을 포함하고, 정규화된 이름 끝의 명령 이름에 glob 스타일 특수 문자를 가질 수 있어요. glob 문자는 네임스페이스 이름에 나타날 수 없어요. 네임스페이스 이름이 완전히 정규화되지 않았으면(네임스페이스 구분자로 시작하지 않으면) "이름 해석" 섹션에서 설명하는 방식으로 네임스페이스 이름으로 해석돼요. 그 이름의 네임스페이스를 찾을 수 없으면 오류예요.
패턴 문자열과 일치하고 현재 네임스페이스에서 내보내진 모든 명령이 현재 네임스페이스에 추가돼요. 이것은 현재 네임스페이스에 원래 네임스페이스의 내보낸 명령을 가리키는 새 명령을 만들어서 수행돼요. 새로 가져온 명령이 호출되면 내보낸 명령을 호출해요. 이 명령은 보통 가져온 명령이 기존 명령과 충돌하면 오류를 돌려줘요. 하지만 -force 옵션을 주면 가져온 명령이 기존 명령을 조용히 대체해요. namespace import 명령은 스냅샷 의미를 가져요. 즉 내보내는 네임스페이스에 현재 정의된 요청된 명령만 가져와요. 다시 말해, namespace import 명령이 실행되는 시점에 네임스페이스에 있는 명령만 가져올 수 있어요. 나중에 그 네임스페이스에서 다른 명령이 정의되고 내보내져도 가져와지지 않아요.
namespace inscope namespace script ?arg ...?
지정된 네임스페이스의 컨텍스트에서 스크립트를 실행해요. 이 명령은 프로그래머가 직접 쓰게 될 것으로 기대되지 않아요. 애플리케이션이 namespace code 명령으로 콜백 스크립트를 만들어 (예: Tk 위젯에) 등록할 때 암시적으로 호출이 생성돼요. namespace inscope 명령은 namespace eval과 많이 비슷하지만, 네임스페이스가 이미 존재해야 하고, 추가 args를 적절한 리스트 요소로 추가해요.
namespace inscope ::foo $script $x $y $z
는 아래와 동등해요.
namespace eval ::foo [concat $script [list $x $y $z]]
따라서 namespace eval과 달리 추가 인수가 두 번째 치환 라운드를 겪지 않아요.
namespace origin command
가져온 명령 command가 가리키는 원래 명령의 정규화된 이름을 돌려줘요. 명령이 네임스페이스로 가져와질 때, 내보내는 네임스페이스의 실제 명령을 가리키는 새 명령이 만들어져요. 명령이 연속된 네임스페이스 a, b, ..., n으로 가져와졌는데 각 네임스페이스가 그저 이전 네임스페이스의 명령을 가져온다면, 이 명령은 첫 네임스페이스 a의 원래 명령의 정규화된 이름을 돌려줘요. command가 가져온 명령을 가리키지 않으면, 명령 자신의 정규화된 이름을 돌려줘요.
namespace parent ?namespace?
namespace의 부모 네임스페이스의 정규화된 이름을 돌려줘요. namespace를 지정하지 않으면 현재 네임스페이스의 부모의 정규화된 이름을 돌려줘요.
namespace path ?namespaceList?
현재 네임스페이스의 명령 해석 경로를 돌려줘요. namespaceList를 이름 붙은 네임스페이스들의 리스트로 지정하면, 현재 네임스페이스의 명령 해석 경로가 그 네임스페이스들로 설정되고 빈 리스트를 돌려줘요. 기본 명령 해석 경로는 항상 비어 있어요. 이름 해석 규칙에 대한 설명은 "이름 해석" 섹션을 참고해요.
namespace qualifiers string
string의 앞쪽 네임스페이스 한정자를 돌려줘요. 한정자는 이중 콜론(::)으로 구분된 네임스페이스 이름이에요. ::foo::bar::x 문자열에 대해 ::foo::bar를 돌려주고, ::에 대해선 빈 문자열을 돌려줘요. 이 명령은 namespace tail 명령의 보완이에요. 네임스페이스 이름이 실제로 현재 정의된 네임스페이스의 이름인지는 확인하지 않아요.
namespace tail string
한정된 문자열 끝의 단순 이름을 돌려줘요. 한정자는 이중 콜론(::)으로 구분된 네임스페이스 이름이에요. ::foo::bar::x 문자열에 대해 x를 돌려주고, ::에 대해선 빈 문자열을 돌려줘요. 이 명령은 namespace qualifiers 명령의 보완이에요. 네임스페이스 이름이 실제로 현재 정의된 네임스페이스의 이름인지는 확인하지 않아요.
namespace upvar namespace ?otherVar myVar ...?
현재 프로시저의 지역 변수 0개 이상이 namespace의 변수를 가리키도록 준비해요. 네임스페이스 이름은 "이름 해석" 섹션에서 설명한 대로 해석돼요. namespace upvar $ns a b 명령은 한정된 네임스페이스 또는 변수 이름에 사용되는 해석 규칙만 빼면 upvar 0 ${ns}::a b와 같은 동작을 해요. namespace upvar는 빈 문자열을 돌려줘요.
namespace unknown ?script?
현재 네임스페이스의 unknown 명령 핸들러를 설정하거나 반환해요. 핸들러는 네임스페이스 안에서 호출된 명령이 현재 네임스페이스, 네임스페이스의 경로, 전역 네임스페이스에서도 찾을 수 없을 때 호출돼요. script 인수가 주어지면 명령 이름과 선택적 인수를 나타내는 잘 구성된 리스트여야 해요. 핸들러가 호출되면 전체 호출 줄이 script에 추가되고 그 결과가 네임스페이스 컨텍스트에서 평가돼요. 모든 네임스페이스의 기본 핸들러는 ::unknown이에요. 인수가 없으면 현재 네임스페이스의 핸들러를 돌려줘요.
namespace which ?-command? ?-variable? name
name을 명령이나 변수로 찾아 그 정규화된 이름을 돌려줘요. 예를 들어 name이 현재 네임스페이스에 없지만 전역 네임스페이스에는 있다면, 이 명령은 전역 네임스페이스의 정규화된 이름을 돌려줘요. 명령이나 변수가 존재하지 않으면 빈 문자열을 돌려줘요. 변수가 variable 명령이나 변수의 트레이스를 통해 생성되었지만 정의되지는 않았다면, 이 명령은 변수의 정규화된 이름을 돌려줘요. 플래그가 없으면 name을 명령 이름으로 취급해요. 이름 해석 규칙에 대한 설명은 "이름 해석" 섹션을 참고해요.
네임스페이스란 무엇인가?
네임스페이스는 명령과 변수의 집합이에요. 명령과 변수를 캡슐화해서 다른 네임스페이스의 명령·변수와 간섭하지 않게 해 줘요. Tcl은 항상 이런 집합을 하나 가졌는데, 우리는 그것을 전역 네임스페이스(global namespace)라고 불러요. 전역 네임스페이스는 모든 전역 변수와 명령을 담아요.
namespace eval 명령은 새 네임스페이스를 만들 수 있게 해 줘요. 예를 들어:
namespace eval Counter {
namespace export bump
variable num 0
proc bump {} {
variable num
incr num
}
}
이것은 변수 num과 프로시저 bump를 담은 새 네임스페이스를 만들어요. 이 네임스페이스의 명령·변수는 같은 프로그램의 다른 명령·변수와 분리돼 있어요. 예를 들어 전역 네임스페이스에 bump라는 명령이 있더라도, 그건 Counter 네임스페이스의 bump 명령과 달라요.
네임스페이스 변수는 Tcl의 전역 변수와 비슷해요. 네임스페이스의 프로시저 밖에 존재하지만, 위 예제처럼 variable 명령으로 프로시저 안에서 접근할 수 있어요.
네임스페이스는 동적이에요. 언제든 명령과 변수를 추가·삭제할 수 있으므로, 일련의 namespace eval 명령으로 네임스페이스 내용을 시간에 따라 구성할 수 있어요. 예를 들어 다음 명령 시리즈는 위의 네임스페이스 정의와 같은 효과를 가져요:
namespace eval Counter {
variable num 0
proc bump {} {
variable num
return [incr num]
}
}
namespace eval Counter {
proc test {args} {
return $args
}
}
namespace eval Counter {
rename test ""
}
test 프로시저가 Counter 네임스페이스에 추가되고, 나중에 rename 명령으로 제거된다는 점을 기억하세요.
네임스페이스는 그 안에 다른 네임스페이스를 가질 수 있어서 계층적으로 중첩돼요. 중첩 네임스페이스는 부모 네임스페이스 안에 캡슐화되어 다른 네임스페이스와 간섭할 수 없어요.
정규화된 이름
각 네임스페이스는 history나 ::safe::interp 같은 텍스트 이름을 가져요. 네임스페이스는 중첩될 수 있으므로, 정규화된 이름(qualified name)으로 네임스페이스 안에 포함된 명령·변수·자식 네임스페이스를 참조해요. 정규화된 이름은 Unix 파일이나 Tk 위젯의 계층 경로 이름과 비슷한데, / 대신 ::을 구분자로 사용해요. 최상위 또는 전역 네임스페이스의 이름은 ""(빈 문자열)이고, ::도 동의어예요.
예를 들어 ::safe::interp::create는 전역 네임스페이스 ::의 자식인 ::safe 네임스페이스의 자식인 interp 네임스페이스 안의 create 명령을 가리켜요.
다른 네임스페이스의 명령·변수에 접근하려면 추가 문법을 써야 해요. 이름은 그것을 담고 있는 네임스페이스로 한정되어야 해요. 전역 네임스페이스에서 Counter 프로시저에 이렇게 접근할 수 있어요:
Counter::bump 5
Counter::Reset
현재 카운트는 이렇게 접근할 수 있어요:
puts "count = $Counter::num"
네임스페이스가 다른 네임스페이스를 포함하면, 그 요소에 도달하기 위해 한정자가 하나 이상 필요할 수 있어요. Counter 네임스페이스를 포함하는 Foo 네임스페이스가 있다면, 전역 네임스페이스에서 bump 프로시저를 이렇게 호출할 수 있어요:
Foo::Counter::bump 3
명령을 만들거나 이름을 바꿀 때도 정규화된 이름을 쓸 수 있어요. 예를 들어 Foo 네임스페이스에 프로시저를 이렇게 추가할 수 있어요:
proc Foo::Test {args} {return $args}
같은 프로시저를 다른 네임스페이스로 옮길 수도 있어요:
rename Foo::Test Bar::Test
정규화된 이름에 대해 남은 몇 가지를 다뤄볼게요. 전역 네임스페이스를 제외한 네임스페이스는 비어 있지 않은 이름을 가져요. ::은 네임스페이스 구분자로 쓰이는 경우를 제외하고 단순 명령·변수·네임스페이스 이름에서 허용되지 않아요. 정규화된 이름의 구분자 부분에서 추가 콜론은 무시돼요. 즉 콜론이 두 개 이상이면 네임스페이스 구분자로 취급돼요. 정규화된 변수·명령 이름의 끝에 오는 ::은 {}라는 이름의 변수·명령을 가리켜요. 하지만 정규화된 네임스페이스 이름의 끝에 오는 ::은 무시돼요.
이름 해석
일반적으로 변수·명령 이름을 받는 모든 Tcl 명령은 정규화된 이름을 지원해요. 즉 set, proc, rename, interp alias 같은 명령에 정규화된 이름을 줄 수 있어요. ::으로 시작하는 완전히 정규화된 이름을 주면, 어떤 명령·변수·네임스페이스를 뜻하는지 의심의 여지가 없어요.
하지만 이름이 ::으로 시작하지 않으면(즉 상대적이면) Tcl은 기본 규칙을 따라 찾아요:
- 변수 이름은 항상 먼저 현재 네임스페이스에서, 그다음 전역 네임스페이스에서 찾는다.
- 명령 이름은 항상 현재 네임스페이스에서 먼저 찾는다. 없으면 현재 네임스페이스의 명령 경로(기본적으로 비어 있음)의 모든 네임스페이스에서 찾는다. 거기도 없으면 전역 네임스페이스에서 찾는다(그래도 없으면 해당 네임스페이스 unknown 핸들러가 처리한다).
- 네임스페이스 이름은 항상 현재 네임스페이스에서만 찾는다.
다음 예제에서:
set traceLevel 0
namespace eval Debug {
printTrace $traceLevel
}
Tcl은 traceLevel을 네임스페이스 Debug에서 찾고, 그다음 전역 네임스페이스에서 찾아요. printTrace 명령도 같은 방식으로 찾아요. 어느 컨텍스트에서도 변수·명령 이름을 찾지 못하면 그 이름은 정의되지 않은 것이에요.
이 점을 분명히 하기 위해 다음 예제를 보세요:
set traceLevel 0
namespace eval Foo {
variable traceLevel 3
namespace eval Debug {
printTrace $traceLevel
}
}
여기서 Tcl은 traceLevel을 먼저 Foo::Debug 네임스페이스에서 찾아요. 거기에 없으므로 그다음 전역 네임스페이스에서 찾아요. 변수 Foo::traceLevel은 이름 해석 과정에서 완전히 무시돼요.
이름 해석에 대한 의문을 해소하려면 namespace which 명령을 쓸 수 있어요. 예를 들어 명령:
namespace eval Foo::Debug {namespace which -variable traceLevel}
은 ::traceLevel을 돌려줘요. 반면 다음 명령:
namespace eval Foo {namespace which -variable traceLevel}
은 ::Foo::traceLevel을 돌려줘요.
앞서 언급했듯이, 네임스페이스 이름은 변수·명령의 이름과 다르게 찾아요. 네임스페이스 이름은 항상 현재 네임스페이스에서 해석돼요. 예를 들어 새 네임스페이스를 만드는 namespace eval 명령은, 새 네임스페이스 이름이 ::으로 시작하지 않는 한 항상 현재 네임스페이스의 자식을 만들어요.
Tcl은 참조할 수 있는 변수·명령·네임스페이스를 제한하는 접근 제어가 없어요. 위 이름 해석 규칙으로 요소로 해석되는 정규화된 이름을 주면 그 요소에 접근할 수 있어요.
같은 네임스페이스의 프로시저에서 variable 명령으로 네임스페이스 변수에 접근할 수 있어요. global 명령과 비슷하게, 이것은 네임스페이스 변수에 대한 지역 링크를 만들어요. 필요하면 현재 네임스페이스에 변수를 만들고 초기화하기도 해요. global 명령은 전역 네임스페이스의 변수에만 링크를 만든다는 점을 기억하세요. 적절한 정규화된 이름으로 항상 네임스페이스 변수를 참조한다면 variable 명령을 쓸 필요가 없어요.
명령 가져오기
네임스페이스는 종종 라이브러리를 나타내는 데 쓰여요. 어떤 라이브러리 명령은 너무 자주 사용되어 정규화된 이름을 매번 입력하기가 번거로워요. 예를 들어 BLT 같은 패키지의 모든 명령이 Blt라는 네임스페이스에 담겨 있다고 해 보죠. 그런 명령들은 이렇게 접근할 수 있어요:
Blt::graph .g -background red
Blt::table . .g 0,0
graph와 table 명령을 자주 사용한다면, Blt:: 접두사 없이 접근하고 싶을 거예요. 명령을 현재 네임스페이스로 가져와서 그렇게 할 수 있어요:
namespace import Blt::*
이것은 Blt 네임스페이스의 모든 내보낸 명령을 현재 네임스페이스 컨텍스트에 추가해서, 이렇게 코드를 쓸 수 있게 해 줘요:
graph .g -background red
table . .g 0,0
namespace import 명령은 namespace export 명령으로 내보낸 네임스페이스의 명령만 가져와요.
네임스페이스에서 모든 명령을 가져오는 것은 일반적으로 좋지 않은 생각이에요. 무엇을 얻게 될지 모르기 때문이에요. 필요한 특정 명령만 가져오는 것이 더 좋아요. 예를 들어:
namespace import Blt::graph Blt::table
은 graph와 table 명령만 현재 컨텍스트로 가져와요.
이미 존재하는 명령을 가져오려 하면 오류가 나요. 이것은 두 다른 패키지에서 같은 명령을 가져오는 것을 막아 줘요. 하지만 가끔(디버깅할 때처럼) 이 제한을 우회하고 싶을 수 있어요. 네임스페이스에 새로 나타난 명령을 가져오기 위해 namespace import 명령을 다시 실행하고 싶을 수도 있어요. 그럴 땐 -force 옵션을 쓸 수 있고, 기존 명령이 조용히 덮어써져요:
namespace import -force Blt::graph Blt::table
어떤 이유로 가져온 명령 사용을 멈추고 싶다면 namespace forget 명령으로 제거할 수 있어요:
namespace forget Blt::*
이것은 Blt에서 가져온 명령이 현재 네임스페이스에 있는지 검색해요. 있으면 제거하고, 없으면 아무것도 하지 않아요. 이후에는 Blt:: 접두사로만 BLT 명령에 접근해야 해요.
내보내는 네임스페이스에서 명령을 이렇게 삭제하면:
rename Blt::graph ""
그 명령은 그것을 가져온 모든 네임스페이스에서 자동으로 제거돼요.
명령 내보내기
네임스페이스에서 명령을 이렇게 내보낼 수 있어요:
namespace eval Counter {
namespace export bump reset
variable Num 0
variable Max 100
proc bump {{by 1}} {
variable Num
incr Num $by
Check
return $Num
}
proc reset {} {
variable Num
set Num 0
}
proc Check {} {
variable Num
variable Max
if {$Num > $Max} {
error "too high!"
}
}
}
프로시저 bump와 reset은 내보내졌으므로, 이렇게 Counter 네임스페이스에서 가져올 때 포함돼요:
namespace import Counter::*
하지만 Check 프로시저는 내보내지지 않았으므로 가져오기 동작에서 무시돼요.
namespace import 명령은 네임스페이스가 내보낸 명령으로 선언된 것만 가져와요. namespace export 명령은 다른 네임스페이스가 가져올 수 있는 명령을 지정해요. namespace import 명령이 내보내지지 않은 명령을 지정하면 그 명령은 가져와지지 않아요.
스코프된 스크립트
namespace code 명령은 스크립트가 만들어진 네임스페이스가 아닌 다른 네임스페이스에서 평가되도록 포장하는 수단이에요. 이벤트 핸들러, Tk 바인딩, 전역 컨텍스트에서 평가할 트레이스를 만드는 데 가장 자주 쓰여요. 예를 들어 다음 코드는 변수 트레이스 콜백을 현재 네임스페이스로 향하게 하는 방법을 보여줘요:
namespace eval a {
variable b
proc theTraceCallback { n1 n2 op } {
upvar 1 $n1 var
puts "the value of $n1 has changed to $var"
return
}
trace add variable b write [namespace code theTraceCallback]
}
set a::b c
실행하면 다음 메시지를 출력해요:
the value of a::b has changed to c
앙상블
namespace ensemble은 앙상블 명령을 만들고 다루는 데 사용돼요. 앙상블 명령은 서브명령을 묶어 만든 명령이에요. 명령은 보통 앙상블이 만들어질 때의 현재 네임스페이스에서 오지만, 이것은 설정 가능해요. 어떤 네임스페이스와도 앙상블이 몇 개든 연관될 수 있으며(없는 경우도 가능, 모든 네임스페이스가 기본적으로 그러함), 네임스페이스가 삭제될 때 그 네임스페이스와 연관된 모든 앙상블도 삭제돼요. 앙상블 명령과 네임스페이스 사이의 연결은 앙상블이 이름을 바꿔도 유지돼요.
namespace ensemble 명령의 세 서브명령이 정의돼 있어요:
namespace ensemble create ?option value ...?
현재 네임스페이스에 연결된 새 앙상블 명령을 만들어, 만들어진 명령의 정규화된 이름을 돌려줘요. namespace ensemble create의 인수들은 namespace ensemble configure 명령처럼 명령을 설정할 수 있게 해 줘요. -command 옵션으로 덮어쓰지 않으면, 이 명령은 연결된 네임스페이스와 정확히 같은 이름의 앙상블을 만들어요. 지원되는 옵션 전체 목록과 효과는 "앙상블 옵션" 섹션을 참고해요.
namespace ensemble configure command ?option? ?value ...?
command라는 앙상블 명령과 연관된 옵션의 값을 가져오거나, 그 앙상블 명령과 연관된 일부 옵션을 갱신해요. 지원되는 옵션 전체 목록과 효과는 "앙상블 옵션" 섹션을 참고해요.
namespace ensemble exists command
command가 존재하고 앙상블 명령인지 설명하는 불리언 값을 돌려줘요. 이 명령은 인수 개수가 틀린 경우에만 오류를 돌려줘요.
앙상블 명령이 호출되면 첫 번째 인수를 가져와 (아래 규칙에 따라) 앙상블 명령과 서브명령을 대체할 단어 목록을 찾아요. 결과 단어 목록은 (추가 치환 없이) 원래 입력된 것처럼 평가돼요(즉 Tcl_EvalObjv로 단어 목록을 전달함) 그리고 명령 결과가 돌려줘요. 앙상블 재작성의 대상이 다른(또는 같은) 앙상블 명령이어도 된다는 점을 기억하세요. 앙상블 명령은 uplevel이나 info level 명령을 통해서는 보이지 않아요.
앙상블 옵션
다음 옵션들은 namespace ensemble create와 namespace ensemble configure 명령이 지원하며, 앙상블 명령이 어떻게 동작하는지 제어해요.
-map
비어 있지 않으면, 서브명령 이름을 대체할 접두사 단어 목록으로의 매핑을 제공하는 사전(dictionary)을 공급해요(interp alias로 만든 별칭과 비슷한 방식으로; 단어는 치환 후 다시 파싱되지 않아요). 설정할 때 어떤 대상의 첫 단어도 완전히 한정되지 않으면 현재 네임스페이스 기준으로 가정하고 정확히 그렇게 바꿔요(즉 읽을 때는 항상 완전히 한정됨). 이 옵션이 비어 있으면 매핑은 서브명령의 지역 이름에서 완전히 한정된 이름으로 돼요. 이 옵션이 비어 있지 않고 -subcommands 옵션이 비어 있으면, 앙상블 서브명령 이름은 정확히 사전에 매핑이 있는 단어들로 돼요.
-parameters
앙상블을 호출하는 사람이 앙상블 이름과 서브명령 인수 사이에 전달하는 이름 붙은 인수(오류 메시지 생성에 이름이 사용됨)의 목록을 제공해요. 기본값은 빈 리스트예요.
-prefixes
(기본적으로 활성화된) 이 옵션은 앙상블 명령이 서브명령의 모호하지 않은 접두사를 인식할지 제어해요. 꺼져 있으면 앙상블 명령은 서브명령 이름의 정확한 일치를 요구해요.
-subcommands
비어 있지 않으면 이 옵션은 앙상블에 정확히 어떤 서브명령이 있는지 나열해요. 각 명령의 매핑은 -map 옵션에 정의된 것이거나, 앙상블에 연결된 네임스페이스의 같은 이름 명령이에요. 이 옵션이 비어 있으면, 네임스페이스의 서브명령은 -map 옵션에 나열된 사전의 키들이거나, 앙상블 명령 호출 시점에 연결된 네임스페이스의 내보낸 명령들이에요.
-unknown
비어 있지 않으면, 앙상블 서브명령이 인식되지 않아 오류를 만들게 될 경우를 처리하는 부분 명령을 제공해요(앙상블의 완전히 한정된 이름을 포함한, 앙상블 명령에 대한 인수인 모든 단어가 추가됨). 비어 있으면(기본값) 앙상블이 특정 서브명령을 어떻게 구현할지 결정할 수 없을 때마다 Tcl_GetIndexFromObj 스타일의 오류가 생성돼요. 자세한 내용은 "unknown 핸들러 동작"을 참고해요.
namespace ensemble create는 다음 추가 옵션을 허용해요:
-command
이 쓰기 전용 옵션은 namespace ensemble create로 만든 앙상블의 이름이 기존 네임스페이스의 어떤 것이든 될 수 있게 해 줘요. 이 옵션의 기본값은 namespace ensemble create 명령이 호출된 네임스페이스의 정규화된 이름이에요.
namespace ensemble configure는 다음 추가 옵션을 허용해요:
-namespace
이 읽기 전용 옵션은 앙상블이 만들어진 네임스페이스의 정규화된 이름을 가져올 수 있게 해 줘요.
unknown 핸들러 동작
앙상블에 unknown 핸들러가 지정되면, 앙상블 명령이 어떤 서브명령을 호출할지 결정하지 못해 오류를 돌려주게 될 때 그 핸들러가 호출돼요. 이것이 발생하는 정확한 조건은 위에서 설명한 -subcommands, -map, -prefixes 옵션으로 제어돼요.
unknown 핸들러를 실행하기 위해 앙상블 메커니즘은 지정된 -unknown 옵션을 가져와 시도된 앙상블 명령 호출의 각 인수를(완전히 한정된 이름으로 표현된 앙상블 명령 자신을 포함해) 추가해요. 결과 명령을 시도된 호출의 스코프에서 호출해요. unknown 핸들러의 실행이 정상적으로 끝나면, 앙상블 엔진은 서브명령을 다시 파싱하고(아래 설명) 다시 디스패치하려 시도해요. 이것은 unknown 서브명령 핸들러에 의해 앙상블 설정이 갱신된 경우에 이상적이에요. unknown 핸들러의 다른 종류의 종료는 오류로 취급돼요.
unknown 핸들러의 결과는 리스트일 것으로 기대돼요(아니면 오류). 리스트가 빈 리스트이면, 앙상블 명령은 원래 서브명령을 다시 찾아보고, 이번에도 찾지 못하면 -unknown 핸들러가 없었던 것처럼 오류가 생성돼요(즉 앙상블의 어떤 특정 호출에 대해 unknown 핸들러는 최대 한 번 호출됨). 이렇게 하면 unknown 핸들러가 앙상블이나 그 배후 네임스페이스를 갱신해 원하는 서브명령의 구현을 제공하고 다시 파싱하기 쉬워져요.
결과가 비어 있지 않은 리스트이면, 그 리스트의 단어들이 -map에서 찾아온 것처럼 앙상블 명령과 서브명령을 대체하는 데 사용돼요. 구현하는 서브명령이 앙상블 명령 호출자의 네임스페이스에 없다면 모든 네임스페이스 한정자를 공급하는 것은 unknown 핸들러 몫이에요. 또 앙상블 명령이 체인으로 연결될 때(예: 텍스트 위젯의 tag·mark 서브명령처럼 앙상블 서브명령을 구현하는 명령 중 하나를 앙상블로 만들면) 재작성이 가장 바깥쪽 앙상블의 호출자 컨텍스트에서 일어난다는 점을 기억하세요. 즉 앙상블 자체는 Tcl 호출 스택에 네임스페이스 컨텍스트를 두지 않아요.
빈 -unknown 핸들러가 주어지면(기본값) 앙상블 명령은 앙상블이 정의한 명령 목록(뒤의 Tcl_GetIndexFromObj 오류 메시지와 비슷한 형식)을 바탕으로 오류 메시지를 생성해요. 서브명령이 다시 파싱 중 여전히 인식되지 않으면 이 오류가 발생해요. -unknown 핸들러가 자신의 네임스페이스를 삭제하는 것도 오류예요.
예제
변수와 내보낸 명령을 담은 네임스페이스를 만들어요:
namespace eval foo {
variable bar 0
proc grill {} {
variable bar
puts "called [incr bar] times"
}
namespace export grill
}
이전 예제에서 정의한 명령을 여러 방법으로 호출해요.
# Direct call
::foo::grill
# Use the command resolution path to find the name
namespace eval boo {
namespace path ::foo
grill
}
# Import into current namespace, then call local alias
namespace import foo::grill
grill
# Create two ensembles, one with the default name and one with a
# specified name. Then call through the ensembles.
namespace eval foo {
namespace ensemble create
namespace ensemble create -command ::foobar
}
foo grill
foobar grill
이전 예제에서 가져온 명령이 어디서 왔는지 찾아요:
puts "grill came from [namespace origin grill]"
현재 네임스페이스에서 가져온 모든 명령을 제거해요:
namespace forget {*}[namespace import]
-parameters 옵션을 사용해 연산자를 첫 번째와 두 번째 인수 사이에 둘 수 있게 하는 숫자 작업용 앙상블을 만들어요.
namespace eval do {
namespace export *
namespace ensemble create -parameters x
proc plus {x y} {expr { $x + $y }}
proc minus {x y} {expr { $x - $y }}
}
# In use, the ensemble works like this:
puts [do 1 plus [do 9 minus 7]]
더 알아보기
interp— 인터프리터 생성·관리upvar,variable— 변수 연결·생성