@call

@call

함수를 호출하는 빌트인인데, 소괄호로 감싼 호출과 같은 일을 하면서도 호출 방식을 더 세밀하게 제어할 수 있어요. 어디에 쓸 수 있는지 보여드릴게요.

출처: Zig Documentation

본문

@callmodifier 를 받아서 함수를 호출해요. 시그니처는 이렇게 생겼어요.

@call(modifier: std.lang.CallModifier, function: anytype, args: anytype) anytype

함수를 호출한다는 점은 표현식을 소괄호로 감싸서 호출하는 방식과 같아요.

const expectEqual = @import("std").testing.expectEqual;

test "noinline function call" {
    try expectEqual(12, @call(.auto, add, .{ 3, 9 }));
}

fn add(a: i32, b: i32) i32 {
    return a + b;
}

실행 결과는 이렇게 나와요.

$ zig test test_call_builtin.zig
1/1 test_call_builtin.test.noinline function call...OK
All 1 tests passed.

일반 함수 호출 문법보다 @call 이 유연한 이유는, 호출 방식을 결정하는 CallModifier 열거형을 첫 인자로 넘길 수 있기 때문이에요. 그 열거형을 그대로 옮기면 아래와 같아요.

pub const CallModifier = enum {
    /// Equivalent to function call syntax.
    auto,

    /// Prevents tail call optimization. This guarantees that the return
    /// address will point to the callsite, as opposed to the callsite's
    /// callsite. If the call is otherwise required to be tail-called
    /// or inlined, a compile error is emitted instead.
    never_tail,

    /// Guarantees that the call will not be inlined. If the call is
    /// otherwise required to be inlined, a compile error is emitted instead.
    never_inline,

    /// Asserts that the function call will not suspend. This allows a
    /// non-async function to call an async function.
    no_suspend,

    /// Guarantees that the call will be generated with tail call optimization.
    /// If this is not possible, a compile error is emitted instead.
    always_tail,

    /// Guarantees that the call will be inlined at the callsite.
    /// If this is not possible, a compile error is emitted instead.
    always_inline,

    /// Evaluates the call at compile-time. If the call cannot be completed at
    /// compile-time, a compile error is emitted instead.
    compile_time,
};

각 modifier가 하는 일을 간단히 짚어볼게요.

  • auto — 일반 함수 호출 문법과 동일해요.
  • never_tail — 꼬리 호출 최적화를 막아요. 반환 주소가 호출 지점(callsite)을 가리키는 걸 보장하죠. 만약 꼬리 호출이나 인라인이 강제되어야 하는 상황이라면 컴파일 에러가 나요.
  • never_inline — 호출이 인라인되지 않음을 보장해요. 인라인이 강제되는 상황이면 역시 컴파일 에러가 나요.
  • no_suspend — 함수 호출이 suspend되지 않는다고 단언해요. 비동기(async)가 아닌 함수가 async 함수를 호출할 수 있게 해주죠.
  • always_tail — 꼬리 호출 최적화로 생성됨을 보장해요. 불가능하면 컴파일 에러가 나요.
  • always_inline — 호출 지점에서 인라인됨을 보장해요. 불가능하면 컴파일 에러가 나요.
  • compile_time — 호출을 컴파일 타임에 평가해요. 컴파일 타임에 끝낼 수 없으면 컴파일 에러가 나요.

더 알아보기

  • 함수 호출과 표현식 호출 문법에 대한 보통의 사용법
  • @call 의 modifier가 활용되는 비동기(async)와 꼬리 호출 최적화