Function Reflection
Function Reflection (함수 반영)
함수가 어떤 타입을 받고, 어떤 타입을 돌려주는지, 함수 자체가 제네릭인지를 컴파일 타임에 궁금할 때가 있어요. Zig에서는 @typeInfo를 쓰면 함수의 시그니처 정보를 컴파일 타임에 그대로 들여다볼 수 있습니다. 이번에는 함수 반영(Function Reflection)으로 함수의 매개변수 타입, 반환 타입, 제네릭 여부를 확인하는 방법을 살펴볼게요.
본문
@typeInfo(@TypeOf(fn))처럼 함수를 @typeOf로 감싸면 그 결과의 @"fn" 필드에 함수의 정보가 담겨요. func.type도 보통 이만한 정보를 담지 못하는데, Zig의 반영은 함수 하나에서 매개변수 타입 배열(param_types), 반환 타입(return_type), 제네릭 여부(is_generic) 같은 정보를 모두 꺼낼 수 있습니다. 실제로 확인하는 코드부터 볼게요.
test_fn_reflection.zig
const std = @import("std");
const math = std.math;
const testing = std.testing;
test "fn reflection" {
try testing.expectEqual(bool, @typeInfo(@TypeOf(testing.expect)).@"fn".param_types[0].?);
try testing.expectEqual(testing.TmpDir, @typeInfo(@TypeOf(testing.tmpDir)).@"fn".return_type.?);
try testing.expect(@typeInfo(@TypeOf(math.Log2Int)).@"fn".is_generic);
}
$ zig test test_fn_reflection.zig
1/1 test_fn_reflection.test.fn reflection...OK
All 1 tests passed.
코드를 하나씩 보면, @TypeOf(testing.expect)로 expect 함수의 타입을 얻은 다음 @typeInfo로 그 타입 정보를 펼쳐요. 그 중 @"fn"은 함수 반영 정보를 담은 구간이고, 여기서 param_types[0]로 첫 번째 매개변수의 타입을 볼 수 있어요. bool, 그러니까 expect가 bool 값을 첫 인자로 받는다는 뜻이죠. 매개변수 타입이 optional로 담겨 있기 때문에 .?로 실제 타입을 꺼내서 비교합니다.
반환 타입도 마찬가지예요. testing.tmpDir은 TmpDir 타입을 돌려주는 함수라서, @"fn".return_type.?이 testing.TmpDir과 같음을 확인할 수 있어요. 마지막으로 math.Log2Int 같은 함수는 제네릭이라서, @"fn".is_generic이 true로 나옵니다. 이렇게 컴파일 타임에 함수의 모양을 살펴볼 수 있으니, 제네릭 함수나 라이브러리 코드에서 함수를 인자로 받는 코드를 짤 때 유용하게 쓸 수 있어요.