Function Reflection

Function Reflection (함수 반영)

함수가 어떤 타입을 받고, 어떤 타입을 돌려주는지, 함수 자체가 제네릭인지를 컴파일 타임에 궁금할 때가 있어요. Zig에서는 @typeInfo를 쓰면 함수의 시그니처 정보를 컴파일 타임에 그대로 들여다볼 수 있습니다. 이번에는 함수 반영(Function Reflection)으로 함수의 매개변수 타입, 반환 타입, 제네릭 여부를 확인하는 방법을 살펴볼게요.

출처: Zig Documentation

본문

@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, 그러니까 expectbool 값을 첫 인자로 받는다는 뜻이죠. 매개변수 타입이 optional로 담겨 있기 때문에 .?로 실제 타입을 꺼내서 비교합니다.

반환 타입도 마찬가지예요. testing.tmpDirTmpDir 타입을 돌려주는 함수라서, @"fn".return_type.?testing.TmpDir과 같음을 확인할 수 있어요. 마지막으로 math.Log2Int 같은 함수는 제네릭이라서, @"fn".is_generictrue로 나옵니다. 이렇게 컴파일 타임에 함수의 모양을 살펴볼 수 있으니, 제네릭 함수나 라이브러리 코드에서 함수를 인자로 받는 코드를 짤 때 유용하게 쓸 수 있어요.

더 알아보기

  • @typeInfo — 타입의 메타데이터를 컴파일 타임에 조회하는 내장 함수
  • @TypeOf — 값의 타입을 얻는 내장 함수
  • comptime — 반영 정보를 활용해 컴파일 타임에 평가하는 문맥
  • Functions — 함수 선언과 제네릭 함수