コンパイル時の関数実行

コンピューティングにおいてコンパイル時関数実行(またはコンパイル時関数評価、あるいは一般的な定数式)とは、通常は関数を機械語にコンパイルし実行時実行するコンパイラが、コンパイル時に関数を実行する機能のことです。これは、関数の引数がコンパイル時に既知であり、関数がグローバル状態を参照したり変更したりしない場合(つまり、純粋関数の場合)に可能です。

一部の引数の値のみがわかっている場合でも、コンパイラはコンパイル時に関数実行 (部分評価)をある程度実行できる可能性があり、引数がわかっていない場合よりも最適化されたコードが生成される可能性もあります。

Lisp

Lispマクロ システムは、同じ言語でユーザー定義関数のコンパイル時評価を使用する初期の例です。

C++

C++のメタコード拡張(Vandevoorde 2003)[1]は、C++テンプレートメタプログラミングの改良構文としてコンパイル時関数評価(CTFE)とコードインジェクションを可能にする初期の実験的なシステムでした

以前のバージョンのC++では、コンパイル時に値を計算するために、次のようなテンプレート メタプログラミングがよく使用されていました。

テンプレート< int N >構造体Factorial { enum { value = N * Factorial < N - 1 >:: value }; };                テンプレート<>構造体Factorial < 0 > { enum { value = 1 }; };           //階乗<4>::値 == 24 //階乗<0>::値 == 1 void foo () { int x =階乗<0> ::; // == 1 int y =階乗<4> ::; // == 24 }            

コンパイル時の関数評価を使用すると、階乗を計算するために使用されるコードは、たとえば C++11 constexpr を使用して実行時の評価用に記述するものと似ています。

stdをインポートします constexpr int階乗( int n ) {戻り値n ? ( n *階乗( n - 1 )) : 1 ; }                constexpr int f10 =階乗( 10 );    int main () { std :: println ( "{}" , f10 ); 0を返す; }      

C++11では、この手法は一般化定数式constexpr)として知られています。[2] 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++)

C++20では、即時関数が導入され、コンパイル時の関数実行はconstexpr制限が緩和され、よりアクセスしやすく柔軟になりました。

// コンパイル時の反復階乗。consteval int factorial ( int n ) { int result = 1 ; while ( n > 1 ) { result * = n -- ; } return result ; }                   int main () { int f4 =階乗( 4 ); // f4 == 24 }       

Factorial関数はとマークされているためconsteval、明示的に定数評価される別のコンテキストで強制されることなく、コンパイル時に呼び出されることが保証されています。したがって、即時関数の使用は、メタプログラミングやコンパイル時チェック(C++20テキストフォーマットライブラリで使用)において幅広い用途を提供します。

コンパイル時の関数実行で即時関数を使用する例を次に示します。

voidアサーション失敗() {}  consteval void testAssert ( bool b ) { if ( ! b ) { assertionFailed (); } }         consteval void test () { int x = 10 ; testAssert ( x == 10 ); // ok x ++ ; testAssert ( x == 11 ); // ok x -- ; testAssert ( x == 12 ); // ここで失敗します}                     int main () {テスト(); }     

この例では、即時関数が定数式で使用できない関数を呼び出しているため、コンパイルが失敗します。つまり、アサーションが失敗した後にコンパイルが停止します。

典型的なコンパイル エラー メッセージは次のようになります。

関数' int main () ': ' constexpr '内で' test () '展開' constexpr '内で' testAssert ( x == 12 ) '展開中エラー: ' constexpr '以外関数' assertionFailed () ' assertionFailed (); ~~~~~~~~~~~~~~~^~ [ ... ]                         

コンパイル時の引数チェックを可能にするコンストラクターとして即時関数を使用する別の例を次に示します。

stdをインポートします std :: string_viewを使用します voidアサーション失敗() {}  構造体CheckedMessage { string_view msg ;     consteval CheckedMessage ( const char * arg ) : msg { arg } { if ( msg . ends_with ( '!' )) { assertionFailed (); } } };            void sendCalmMessage ( const CheckedMessage & arg ) { std :: println ( "{}" , arg . msg ); }      int main () { sendCalmMessage ( "Hello, world" ); sendCalmMessage ( "Hello, world!" ); }    

ここでコンパイルが失敗し、次のメッセージが表示されます:

関数' int main () ': ' CheckedMessage ((( const char * ) "Hello, world!" ) ) '' constexpr '展開エラー:' constexpr '関数' void assertionFailed ( ) ' assertionFailed (); ~~~~~~~~~~~~~~~ ^~ [ ... ]                    

D

以下はD言語におけるコンパイル時関数評価の例である: [3]

int階乗( int n ) { if ( n == 0 ) { return 1 ; } return n *階乗( n - 1 ); }                 // コンパイル時に計算されるenum y = factorial ( 0 ); // == 1 enum x = factorial ( 4 ); // == 24        

この例では、「factorial」という有効なD関数を指定しています。この関数は通常、実行時に評価されます。 の使用は、enum変数の初期化子がコンパイル時に計算される必要があることをコンパイラに指示します。関数の引数もコンパイル時に解決可能である必要があることに注意してください。[4]

CTFE は、コンパイル時にデータ構造を簡単な方法で設定するために使用できます (D バージョン 2):

int [] genFactorials ( int n ) { int [] result = new int [ n ]; result [ 0 ] = 1 ; foreach ( i ; 1 .. n ) { result [ i ] = result [ i - 1 ] * i ; } return result ; }                           列挙型階乗= 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プログラミング言語におけるコンパイル時関数評価の例である: [5]

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]

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 はコンパイル時に汎用データ構造を作成するために使用できます。

fn List ( comptime T : type ) type { return struct { items : [] T , len : usize , }; }             // 汎用リストデータ構造は、型を渡すことによってインスタンス化できます。var buffer : [ 10 ] i32 = undefined ; var list = List ( i32 ) { . items = & buffer , . len = 0 , };             

参考文献

  1. ^ Daveed Vandevoorde、エジソン デザイン グループ (2003 年 4 月 18 日)。 「C++ におけるリフレクティブ メタプログラミング」(PDF) 2015 年7 月 19 日に取得
  2. ^ Gabriel Dos Reis、Bjarne Stroustrup (2010年3月). 「システムプログラミング言語のための一般定数式。SAC-2010。第25回ACM応用コンピューティングシンポジウム」(PDF) .
  3. ^ D 2.0 言語仕様: 関数
  4. ^ D 2.0 言語仕様: 属性
  5. ^ Zig 0.11.0 言語リファレンス: コンパイル時式
  6. ^ Zig 0.11.0 言語リファレンス: コンパイル時パラメータ
  • さまざまな言語におけるコンパイル時関数評価の Rosettacode の例
Retrieved from "https://en.wikipedia.org/w/index.php?title=Compile-time_function_execution&oldid=1311769424"