맨위로가기

컴파일 타임 함수 실행

"오늘의AI위키"는 AI 기술로 일관성 있고 체계적인 최신 지식을 제공하는 혁신 플랫폼입니다.
"오늘의AI위키"의 AI를 통해 더욱 풍부하고 폭넓은 지식 경험을 누리세요.

1. 개요

컴파일 타임 함수 실행(CTFE)은 컴파일 시간에 함수를 실행하는 프로그래밍 기법이다. 리스프 매크로 시스템이 초기 사례이며, C++에서는 템플릿 메타프로그래밍을 통해 구현되었다. C++11에서 `constexpr` 키워드가 도입되어 일반화되었고, C++14에서 제약이 완화되었다. C++20에서는 `consteval` 키워드를 사용하는 즉시 함수가 도입되어 컴파일 타임에만 실행되는 함수를 명시적으로 정의할 수 있게 되었다. D 언어는 `enum`을 사용하여 CTFE를 지원하며, Zig는 `comptime` 키워드를 통해 지원한다. CTFE는 메타 프로그래밍, 컴파일 타임 검사, 제네릭 데이터 구조 생성 등에 활용된다.

더 읽어볼만한 페이지

  • 컴파일러 최적화 - 데이터 흐름 분석
    데이터 흐름 분석은 프로그램의 제어 흐름 그래프를 바탕으로 변수의 정의, 사용, 생존 여부를 분석하며, 전이 함수와 결합 연산을 통해 데이터 흐름 정보를 계산하고 반복적으로 갱신하여 해를 구한다.
  • 컴파일러 최적화 - 느긋한 계산법
    느긋한 계산법은 계산 결과가 필요할 때까지 계산을 늦추는 방식으로, 함수형 프로그래밍 언어에서 유용하게 사용되며 무한 데이터 구조를 다루거나 불필요한 계산을 피하는 데 효과적이다.
  • 컴파일러 구성 - 구문 분석
    구문 분석은 입력 데이터를 구조화된 형태로 변환하는 과정으로, 컴퓨터 언어에서는 소스 코드를 분석하여 추상 구문 트리를 생성하고, 자연어 처리에서는 텍스트의 문장 구조와 의미를 분석한다.
  • 컴파일러 구성 - 바이너리 재컴파일러
컴파일 타임 함수 실행

2. 역사

컴파일 타임 함수 실행의 초기 개념은 리스프의 매크로 시스템에서 찾을 수 있다. 리스프 매크로는 사용자 정의 함수를 컴파일 타임에 평가하여 코드를 생성하는 기능을 제공했다.

C++의 메타코드 확장(Vandevoorde 2003)은 컴파일 타임 함수 평가(CTFE)와 C++ 템플릿 메타프로그래밍을 위한 개선된 문법으로서 코드 인젝션을 허용하는 실험적인 초기 시스템이었다.[7] C++11에서는 이 기법이 일반화된 상수 표현(constexpr)으로 알려졌으며,[8] C++14에서는 constexpr에 대한 제한이 완화되었다.

2. 1. C++

초기 버전의 C++에서는 템플릿 메타프로그래밍을 통해 컴파일 타임에 값을 계산하는 기법이 사용되었다.[7][1] 예를 들어, 다음과 같이 팩토리얼 값을 컴파일 타임에 계산할 수 있었다.



template struct Factorial {

enum {

value = N * Factorial::value

};

};

template <> struct Factorial<0> {

enum { value = 1 };

};

// Factorial<4>::value == 24

// Factorial<0>::value == 1

void foo() {

int x = Factorial<0>::value; // == 1

int y = Factorial<4>::value; // == 24

}



C++11에서는 `constexpr` 키워드를 도입하여 일반화된 상수 표현식(일반화된 상수 표현식)을 지원하면서 컴파일 타임 함수 실행(CTFE)의 개념이 발전했다.[8][2] `constexpr`를 사용하면 런타임 평가를 위해 작성하는 코드와 유사한 방식으로 컴파일 타임에 팩토리얼을 계산할 수 있다.



#include

constexpr int factorial(int n) {

return n ? (n * factorial(n - 1)) : 1;

}

constexpr int f10 = factorial(10);

int main() {

printf("%d\n", f10);

return 0;

}



C++14에서는 `constexpr`의 제약 조건을 완화하여 지역 변수 선언, 조건문, 반복문 등을 사용할 수 있게 되면서 컴파일 타임 함수 실행이 더욱 유연해졌다. 다음은 C++14에서 컴파일 타임 함수 실행의 예시이다.



// 컴파일 타임에 반복적인 팩토리얼.

constexpr int Factorial(int n) {

int result = 1;

while (n > 1) {

result *= n--;

}

return result;

}

int main() {

constexpr int f4 = Factorial(4); // f4 == 24

}



C++20에서는 `consteval` 키워드를 사용하는 즉시 함수(immediate function)를 도입하여 컴파일 타임에만 실행되는 함수를 명시적으로 정의할 수 있게 되었다. 즉시 함수는 메타 프로그래밍 및 컴파일 타임 검사에 유용하게 활용된다.



// 컴파일 타임에 반복적인 팩토리얼 계산

consteval int Factorial(int n) {

int result = 1;

while (n > 1) {

result *= n--;

}

return result;

}

int main() {

int f4 = Factorial(4); // f4 == 24

}


2. 2. 기타 언어

D 언어는 `enum`을 사용하여 컴파일 타임에 값을 계산하고, CTFE를 통해 데이터 구조를 초기화하거나 문자열을 생성하는 기능을 제공한다.[3][4] 다음은 D 언어에서 컴파일 타임 함수 실행 예시이다.

```D

int factorial(int n) {

if (n == 0)

return 1;

return n * factorial(n - 1);

}

// 컴파일 타임에 계산됨

enum y = factorial(0); // == 1

enum x = factorial(4); // == 24

```

위 예시는 일반적으로 런타임에 평가되는 "factorial"이라는 유효한 D 함수를 지정한다. `enum`을 사용하면 변수의 초기화 값이 컴파일 시간에 계산되어야 함을 컴파일러에 알린다.

CTFE는 간단한 방식으로 컴파일 시간에 데이터 구조를 채우는 데 사용할 수 있다(D 버전 2).

```D

int[] genFactorials(int n) {

auto result = new int[n];

result[0] = 1;

foreach (i; 1 .. n)

result[i] = result[i - 1] * i;

return result;

}

enum factorials = genFactorials(13);

void main() {}

// 'factorials'는 컴파일 타임에 다음을 포함합니다.

// [1, 1, 2, 6, 24, 120, 720, 5_040, 40_320, 362_880, 3_628_800,

// 39_916_800, 479_001_600]

```

CTFE는 D 코드에서 문자열을 생성하는 데 사용될 수 있으며, 생성된 문자열은 D 코드로 구문 분석되고 컴파일된다.

Zig 프로그래밍 언어는 `comptime` 키워드를 사용하여 컴파일 타임 함수 실행을 지원하며, 컴파일 타임 매개변수를 통해 제네릭 데이터 구조를 생성하는 데 활용할 수 있다.[5][6] 다음은 Zig에서 컴파일 타임 함수 실행의 예시이다.

```Zig

pub fn factorial(n: usize) usize {

var result = 1;

for (1..(n + 1)) |i| {

result *= i;

}

return result;

}

pub fn main() void {

const x = comptime factorial(0); // == 0

const y = comptime factorial(4); // == 24

}

```

위 예시는 일반적으로 런타임에 평가될 "factorial"이라는 유효한 Zig 함수를 지정한다. `comptime`을 사용하면 변수의 초기화가 컴파일 시간에 계산되어야 함을 컴파일러에 알린다.

Zig는 또한 컴파일 타임 매개변수를 지원한다.

```Zig

pub fn factorial(comptime n: usize) usize {

var result: usize = 1;

for (1..(n + 1)) |i| {

result *= i;

}

return result;

}

pub fn main() void {

const x = factorial(0); // == 0

const y = factorial(4); // == 24

}

```

CTFE는 컴파일 시간에 제네릭 데이터 구조를 만드는 데 사용할 수 있다.

```Zig

fn List(comptime T: type) type {

return struct {

items: []T,

len: usize,

};

}

// The generic List data structure can be instantiated by passing in a type:

var buffer: [10]i32 = undefined;

var list = List(i32){

.items = &buffer,

.len = 0,

};

3. C++에서의 활용

C++에서는 컴파일 타임 함수 실행을 통해 다양한 기능을 구현할 수 있다.

초기 버전의 C++에서는 템플릿 메타프로그래밍이 컴파일 타임에 값을 계산하기 위해 자주 사용되었다.[7] C++11에서는 이 기법이 일반화된 상수 표현식(`constexpr`)으로 알려졌으며, `constexpr` 함수를 사용하면 런타임과 컴파일 타임 모두에서 실행 가능한 함수를 작성할 수 있다.[8]

C++14에서는 `constexpr`에 대한 제한이 완화되어 로컬 변수 정의, 조건문, 순환문의 사용이 허용되었다.[2] C++20에서는 즉시 함수(immediate function)가 도입되어 컴파일 타임 함수 실행이 더 접근성이 좋고 유연해졌으며, 메타 프로그래밍 및 컴파일 타임 검사에 광범위하게 사용될 수 있다.

3. 1. 템플릿 메타프로그래밍

초기 버전의 C++에서, 템플릿 메타프로그래밍은 컴파일 타임에 값을 계산하기 위해 자주 사용되었다. 다음은 그 예시이다.[7]

```cpp

template

struct Factorial {

enum { value = N * Factorial::value };

};

template <>

struct Factorial<0> {

enum { value = 1 };

};

// Factorial<4>::value == 24

// Factorial<0>::value == 1

void foo() {

int x = Factorial<0>::value; // == 1

int y = Factorial<4>::value; // == 24

}

```

컴파일 타임 함수 실행을 사용하면, 팩토리얼(계승)을 계산하는 데 사용되는 코드는 C++11 `constexpr`을 사용하는 런타임 평가를 위해 쓰는 것과 비슷할 것이다.[8]

3. 2. constexpr

C++11에서, 이 기법은 일반화된 상수 표현식(`constexpr`)으로 알려졌다.[8] `constexpr` 함수를 사용하면 런타임과 컴파일 타임 모두에서 실행 가능한 함수를 작성할 수 있다. C++11 `constexpr`을 사용하는 런타임 평가를 위해 쓰는 코드는 컴파일 타임 함수 평가를 사용하는 코드와 비슷하다.



#include

constexpr int factorial(int n) {

return n ? (n * factorial(n - 1)) : 1;

}

constexpr int f10 = factorial(10);

int main() {

printf("%d\n", f10);

return 0;

}



C++14는 `constexpr`에 대한 제한을 풀어주어, 로컬 변수 정의, 조건문, 순환문의 사용을 허용하였다 (모든 데이터가 실행을 위해 요구되는 일반적인 제한은 컴파일 타임에도 유효하다).[2]

다음은 C++14에서 컴파일 타임 함수 실행의 예시이다.



// 컴파일 타임에 반복적인 팩토리얼.

constexpr int Factorial(int n) {

int result = 1;

while (n > 1) {

result *= n--;

}

return result;

}

int main() {

constexpr int f4 = Factorial(4); // f4 == 24

}


3. 3. consteval (즉시 함수)

C++20에서 즉시 함수(immediate function)가 도입되었고, 완화된 `constexpr` 제한으로 컴파일 타임 함수 실행이 더 접근성이 좋고 유연해졌다.

```cpp

// 컴파일 타임에 반복적인 팩토리얼 계산

consteval int Factorial(int n) {

int result = 1;

while (n > 1) {

result *= n--;

}

return result;

}

int main() {

int f4 = Factorial(4); // f4 == 24

}

```

`Factorial` 함수는 `consteval`로 표시되어 있으므로 컴파일 타임에 호출이 보장된다. 따라서 즉시 함수는 메타 프로그래밍 및 컴파일 타임 검사(C++20 텍스트 서식 라이브러리에서 사용)에 광범위하게 사용될 수 있다.

다음은 컴파일 타임 함수 실행에서 즉시 함수를 사용하는 예이다.

```cpp

void you_see_this_error_because_assertion_fails() {}

consteval void cassert(bool b) {

if (!b)

you_see_this_error_because_assertion_fails();

}

consteval void test() {

int x = 10;

cassert(x == 10); // ok

x++;

cassert(x == 11); // ok

x--;

cassert(x == 12); // 여기서 실패

}

int main() { test(); }

```

이 예에서는 상수 표현식에서 사용할 수 없는 함수를 즉시 함수가 호출했기 때문에 컴파일이 실패한다. 즉, 어설션(assertion)이 실패한 후 컴파일이 중지된다.

일반적인 컴파일 오류 메시지는 다음과 같다.

```cpp

In function 'int main()':

in 'constexpr' expansion of 'test()'

in 'constexpr' expansion of 'cassert(x == 12)'

error: call to non-'constexpr' function 'you_see_this_error_because_assertion_fails()'

you_see_this_error_because_assertion_fails();

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~

[ ... ]

```

다음은 컴파일 타임 인수 검사를 가능하게 하는 생성자로서 즉시 함수를 사용하는 또 다른 예이다.

```cpp

#include

#include

void you_see_this_error_because_the_message_ends_with_exclamation_point() {}

struct checked_message {

std::string_view msg;

consteval checked_message(const char* arg)

: msg(arg) {

if (msg.ends_with('!'))

you_see_this_error_because_the_message_ends_with_exclamation_point();

}

};

void send_calm_message(checked_message arg) {

std::cout << arg.msg << '\n';

}

int main() {

send_calm_message("Hello, world");

send_calm_message("Hello, world!");

}

```

여기서 컴파일은 다음과 같은 메시지와 함께 실패한다.

```cpp

In function 'int main()':

in 'constexpr' expansion of 'checked_message(((const char*)"Hello, world!"))'

error: call to non-'constexpr' function 'void you_see_this_error_because_the_message_ends_with_exclamation_point()'

you_see_this_error_because_the_message_ends_with_exclamation_point();

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~

[ ... ]

4. 다른 프로그래밍 언어에서의 CTFE

D 언어와 Zig 프로그래밍 언어 등 다양한 프로그래밍 언어에서 컴파일 타임 함수 실행(CTFE) 기능을 지원한다.

4. 1. D 언어

D 언어에서는 `enum`을 이용하여 컴파일 타임에 함수를 실행하고, 그 결과를 변수에 할당할 수 있다.[3]

```D

int factorial(int n) {

if (n == 0)

return 1;

return n * factorial(n - 1);

}

// 컴파일 타임에 계산됨

enum y = factorial(0); // == 1

enum x = factorial(4); // == 24

```

위 예시는 일반적으로 런타임에 평가되는 "factorial"이라는 유효한 D 함수를 보여준다. `enum`을 사용하면 변수의 초기화 값이 컴파일 시간에 계산되어야 함을 컴파일러에게 알려준다. 함수의 인수는 컴파일 시점에 결정될 수 있어야 한다.[4]

CTFE를 사용하면 간단한 방법으로 컴파일 시간에 데이터 구조를 채울 수 있다. (D 버전 2):

```D

int[] genFactorials(int n) {

auto result = new int[n];

result[0] = 1;

foreach (i; 1 .. n)

result[i] = result[i - 1] * i;

return result;

}

enum factorials = genFactorials(13);

void main() {}

// 'factorials'는 컴파일 타임에 다음을 포함합니다.

// [1, 1, 2, 6, 24, 120, 720, 5_040, 40_320, 362_880, 3_628_800,

// 39_916_800, 479_001_600]

```

CTFE는 D 코드에서 문자열을 만드는 데 사용할 수 있으며, 만들어진 문자열은 D 코드로 구문 분석되고 컴파일된다.

4. 2. Zig 언어

Zig 프로그래밍 언어에서 컴파일 타임 함수 실행의 예시는 다음과 같다.[5]

```Zig

pub fn factorial(n: usize) usize {

var result = 1;

for (1..(n + 1)) |i| {

result *= i;

}

return result;

}

pub fn main() void {

const x = comptime factorial(0); // == 0

const y = comptime factorial(4); // == 24

}

```

위 예시는 일반적으로 런타임에 평가될 "factorial"이라는 유효한 Zig 함수를 지정한다. `comptime`을 사용하면 변수의 초기화가 컴파일 시간에 계산되어야 함을 컴파일러에 알린다. 함수의 인수는 컴파일 시간에 해결할 수 있어야 한다.

Zig는 또한 컴파일 타임 매개변수를 지원한다.[6]

```Zig

pub fn factorial(comptime n: usize) usize {

var result: usize = 1;

for (1..(n + 1)) |i| {

result *= i;

}

return result;

}

pub fn main() void {

const x = factorial(0); // == 0

const y = factorial(4); // == 24

}

```

컴파일 타임 함수 실행(CTFE)은 컴파일 시간에 제네릭 데이터 구조를 만드는 데 사용할 수 있다.

```Zig

fn List(comptime T: type) type {

return struct {

items: []T,

len: usize,

};

}

// The generic List data structure can be instantiated by passing in a type:

var buffer: [10]i32 = undefined;

var list = List(i32){

.items = &buffer,

.len = 0,

};

참조

[1] 웹사이트 Reflective Metaprogramming in C++ http://www.open-std.[...] 2003-04-18
[2] 웹사이트 General Constant Expressions for System Programming Languages. SAC-2010. The 25th ACM Symposium On Applied Computing. http://www.stroustru[...] 2010-03
[3] 문서 D 2.0 language specification: Functions http://d-programming[...]
[4] 문서 D 2.0 language specification: Attributes http://d-programming[...]
[5] 문서 Zig 0.11.0 Language Reference: Compile-Time Expressions https://ziglang.org/[...]
[6] 문서 Zig 0.11.0 Language Reference: Compile-Time Parameters https://ziglang.org/[...]
[7] 웹인용 Reflective Metaprogramming in C++ http://www.open-std.[...] 2003-04-18
[8] 웹인용 General Constant Expressions for System Programming Languages. SAC-2010. The 25th ACM Symposium On Applied Computing. http://www.stroustru[...] 2010-03



본 사이트는 AI가 위키백과와 뉴스 기사,정부 간행물,학술 논문등을 바탕으로 정보를 가공하여 제공하는 백과사전형 서비스입니다.
모든 문서는 AI에 의해 자동 생성되며, CC BY-SA 4.0 라이선스에 따라 이용할 수 있습니다.
하지만, 위키백과나 뉴스 기사 자체에 오류, 부정확한 정보, 또는 가짜 뉴스가 포함될 수 있으며, AI는 이러한 내용을 완벽하게 걸러내지 못할 수 있습니다.
따라서 제공되는 정보에 일부 오류나 편향이 있을 수 있으므로, 중요한 정보는 반드시 다른 출처를 통해 교차 검증하시기 바랍니다.

문의하기 : help@durumis.com