関数と他のグローバルな宣言

この章では、KLプログラムのグローバルスコープの中で現れる、関数や関数ライクなエンティティ、名前定数、そして require ステートメントのインスタンスを含む、それぞれのエンティティの詳細について触れていきます。構造体とオブジェクトの定義もグローバルスコープの中に現れます。そしてこれらについては、 KLの型システム 節で触れていきます。

関数

関数はプログラムの他の部分から呼ばれるプログラム文の集まりです。関数はゼロまたはそれ以上の parameters のリストを受け取り、オプションとして、 return value を返します。

関数定義

KLにおける関数定義は、JavaScriptにおける “伝統的な” 関数定義の文法とほとんど同じです。次の重要な違いを除いて:

  • 戻り値の型と関数の各パラメーターの型は明示的に宣言しなければなりません。もし関数が値を返さないのなら、戻り値の型の指定は取り除く必要があります。
  • パラメーター宣言では、追加的に、in を前につけることで、入力(読み込みのみ:デフォルトです)パラメーターとして、または、 io を前につけることで、入出力(読み書き)パラメーターとして宣言することができます。
  • 関数はオプションとして function の代わりに inline キーワードを使って定義することができます。詳細は Inline 関数,メソッド を見てください。
  • function キーワードはオプションです。もし functioninline が指定されなかった場合は、function が指定されたとみなされます。
/*
** Example: Function Definitions
*/

// Function returning a value and using only
// input parameters

function Float32 add(Float32 lhs, Float32 rhs) {
  return lhs + rhs;
}

// Function not returning a value and using both
// input and input-output parameters

function add(in Float32 lhs, in Float32 rhs, io Float32 result) {
  result = lhs + rhs;
}

// The 'function' keyword is totally optional
Float32 double(Float32 x) {
  return 2 * x;
}

operator entry() {
  report(add(2, 2));

  Float32 addResult;
  add(2, 2, addResult);
  report(addResult);

  report(double(2));
}

/*
** Output:

+4.0
+4.0
+4.0

*/

関数呼び出し

関数呼び出し(”コール”)はJavaScriptと同じ文法で作られています。すなわち、関数名に対してコンマ区切りの引数リストを加え、丸カッコで囲みます。

/*
** Example: Function Invocation
*/

function Integer add(Integer lhs, Integer rhs) {
  return lhs + rhs;
}

operator entry() {
  report("2 plus 2 is " + add(2, 2));
}

/*
** Output:

2 plus 2 is 4

*/

関数プロトタイプ

KLにおける function prototype はボディ(関数本体)のない関数宣言です。関数プロトタイプを使うことで、関数の定義より前の位置から、その関数を呼び出すことができます。

  • 二つもしくはそれ以上の関数が互いを呼ぶとき、そのような関数は co-recursive と呼ばれることがあります:
    /*
    ** Example: Co-recursive Functions
    */
    
    // Function prototype for 'two', so that 'one' can call it before it is defined
    function two(Integer n);
    
    // The function 'one' calls 'two' even though it is not yet defined
    function one(Integer n) {
      report("one");
      if (n > 0)
        two(n - 1);
    }
    
    // The definition of the function 'two' comes after its prototype
    function two(Integer n) {
      report("two");
      if (n > 0)
        one(n - 1);
    }
    
    operator entry() {
      one(4);
    }
    
    /*
    ** Output:
    
    one
    two
    one
    two
    one
    
    */
    
  • 関数定義がFabricエクステンションから与えられたものだった場合、そのFabricエクステンション内の関数のシンボル名は、関数プロトタイプに = "symbol name" または = 'symbol name' を追加したものになります。これらは通常、 external functions: と呼ばれます。
    /*
    ** Example: External Functions
    */
    
    // The prototype 'libc_perror' is linked to an external function 'perror'
    function libc_perror(Data cString) = 'perror';
    
    // The KL function 'perror' is what KL functions actually call
    function perror(String string) {
      libc_perror(string.data());
    }
    
    operator entry() {
      perror("something that caused an error");
    }
    

ポリモーフィズム

KLは compile-time function polymorphism をサポートしています。これは、関数のパラメーターの数が異なるか、または、これらのパラメーターで型やin/ioの修飾が異なる場合に限り、同じ名前の複数の関数を持つことができる、というものです。

注釈

全く同じ型のパラメータ群を持つ同じ名前の関数を2つ持とうとするとエラーになります(異なる型を返していたとしてもです)。

関数呼び出しがKLソース内で書かれていた時に、もし同じ名前の複数の関数が存在していたら、その時はKLコンパイラはどの関数を呼び出すべきか決めるためにベストマッチ・システムを使用します。厳格なパラメーターの型のマッチング(訳者注:暗黙のキャストなどを伴わずに、互いに全く同じ型であると判別すること)は常に型キャストより優先して行われます。もしコンパイラが唯一の最適な関数を選べなかった場合は、多義性(曖昧さ)を示すエラーを報告します。

次の例は、関数ポリモーフィズムの一つの使用例を示すものです。

/*
** Example: Function Polymorphism
*/

function display(Integer a) {
  report("integer value is " + a);
}

function display(String s) {
  report("string value is '" + s + "'");
}

operator entry() {
  Integer integer = 42;
  display(integer);

  String string = "hello";
  display(string);

  Byte byte = 64;
  display(byte);
}

/*
** Output:

integer value is 42
string value is 'hello'
integer value is 64

*/

演算子

KLにおける operator キーワードは、関数がFabricディペンデンシー・グラフからKLへのエントリーポイントとして使われるように、その関数をマークするために使用されます。

operator addElements(io Float32 lhs, io Float32 rhs, io Float32 result) {
  result = lhs + rhs;
}

コンストラクタ

ユーザー定義の型の constructor は他の値から、ユーザーが定義した型の値を初期化する関数です。

コンストラクタ宣言

コンストラクタは、ユーザー定義の型と同じ名前の関数として宣言されます。このコンストラクタ関数は任意の個数のパラメーターを取ることができます。ただし、入力パラメーターである必要があります。コンストラクターは入出力パラメーターは取ることができません。また、コンストラクターは値を返せません。

コンストラクタ定義の本体の中では、初期化される値は、 this キーワードによって参照できます。そのメンバーには . (ドット)演算子を使うことでアクセスできます。このコンテキストにおいて、 this は常に読み書き可能です。つまり、メンバーは変更できます。

/*
** Example: Constructor Declarations
*/

struct Complex32 {
  Float32 re;
  Float32 im;
};

// The empty constructor;
function Complex32() {
  this.re = this.im = 0.0;
}

// Construct a Complex from a Float32
function Complex32(Float32 x) {
  this.re = x;
  this.im = 0.0;
}

// Construct a Complex from two Float32s
function Complex32(Float32 x, Float32 y) {
  this.re = x;
  this.im = y;
}

operator entry() {
  report(Complex32());
  report(Complex32(3.141));
  report(Complex32(3.141, 2.718));
}

/*
** Output:

{re:+0.0,im:+0.0}
{re:+3.141,im:+0.0}
{re:+3.141,im:+2.718}

*/

関数のように、コンストラクタはオプションで function の代わりに inline を使って定義することができます。詳しくは Inline 関数,メソッド を参照してください。

コンストラクタ呼び出し

コンストラクタの呼び出し方はいくつかあります。

裸の初期化

ある変数が何の初期化もなしに宣言されたとしたら、 empty constructor (つまり、何のパラメーターも取らないコンストラクタ)がその変数を初期化するために呼び出されます。これを naked initialization と呼びます。

/*
** Example: Naked Initialization
*/

struct MyType {
  Integer n;
  Float32 x;
};

// The empty constructor
function MyType() {
  this.n = 42;
  this.x = 3.141;
}

operator entry() {
  MyType myType; // invokes the empty constructor
  report(myType);
}

/*
** Output:

{n:42,x:+3.141}

*/

代入初期化

もし、(ある型の初期化時に、)それと同じ型の変数が宣言の一部として代入されていた場合、単一パラメーターコンストラクタが呼び出されます。これは assignment initialization と呼ばれます。もし、割り当てられた値の型にぴったりマッチするコンストラクタがなければ、呼び出すべきコンストラクタを選ぶために、ベスト・マッチ・ポリモーフィズムルールが適用されます。

例:

struct MyType {
  String string;
};

// Construct from a string
function MyType(String string) {
  this.string = "The string was '" + string + "'";
}

// Construct from a scalar
function MyType(Float64 float64) {
  this.string = "The float64 was " + float64;
}

operator entry() {
  // Construct MyType from String value
  MyType myTypeFromString = "foo";
  report(myTypeFromString);

  // Construct MyType from Float64 value
  MyType myTypeFromFloat64 = 2.718;
  report(myTypeFromFloat64);

  // There is no constructor that takes a Boolean but
  // there is a cast from Boolean to String
  MyType myTypeFromBoolean = true;
  report(myTypeFromBoolean);
}

出力:

{string:"The string was 'foo'"}
{string:"The float64 was 2.718"}
{string:"The string was 'true'"}

呼び出し初期化

もし、ある変数がその宣言の一部として “呼び出されて”(つまり、関数呼び出しの文法を使って)いた場合、与えられた引数を取る(マッチする)コンストラクターが呼ばれます。これを invocation initialization と呼びます。もし、呼び出し時に渡された引数とぴったりマッチするコンストラクタがない場合は、呼び出すべきコンストラクタを選ぶために、ベスト・マッチ・ポリモーフィズムルールが適用されます。

例:

struct Vec2 {
  Float64 x;
  Float64 y;
};

// Construct from two scalars
function Vec2(Float64 x, Float64 y) {
  this.x = x;
  this.y = y;
}

operator entry() {
  Vec2 vec2FromFloat64s(3.141, 2.718);
  report(vec2FromFloat64s);
  Vec2 vec2FromIntegers(42, -7);  // Uses best-match polymorphism to convert Integer to Float64
  report(vec2FromIntegers);
}

出力:

{x:3.141,y:2.718}
{x:42,y:-7}

一時初期化

もし関数呼び出し時の関数名が型の名前と同じだった場合、与えられた引数を取るコンストラクタは、指定された型の一時的な値を作るために呼び出されます。もし、呼び出し時に渡された引数とぴったりマッチするコンストラクタがない場合は、呼び出すべきコンストラクタを選ぶために、ベスト・マッチ・ポリモーフィズムルールが適用されます。これを temporary initialization と呼びます。

注釈

KLはコンストラクションとキャスティングを区別しません。異なる型への値のキャストは、(与えられた値から適切なコンストラクタを選んで使用することにより、)与えられた型の一時的な値を生成して、それを初期化するのと同じです。

例:

struct Vec2 {
  Float64 x;
  Float64 y;
};

// Construct from two scalars
function Vec2(Float64 x, Float64 y) {
  this.x = x;
  this.y = y;
}

operator entry() {
  report(Vec2(3.141, 2.718));
  report(Vec2(42, -7));  // Uses best-match polymorphism to convert Integer to Float64
}

出力:

{x:3.141,y:2.718}
{x:42,y:-7}

基底型のコンストラクタ(継承)

バージョン 1.13.0 で追加.

特殊化された構造体またはオブジェクト型が基底型から inherits するとき、特殊化された型のコンストラクタより前に、基底型のデフォルトコンストラクタが暗黙的に呼び出されます。

注釈

現時点での制約として、特殊化した型のコンストラクタから、引数を伴って基底クラスのコンストラクタを呼び出すことはできません。次の例では、この問題の回避策として initialize メソッドを使っています。

object Shape {
  Float32 centerX, centerY;
};

inline Shape( Float32 centerX, Float32 centerY ) {
  this.initialize( centerX, centerY );
}

/// \internal
inline Shape.initialize!( Float32 centerX, Float32 centerY ) {
  this.centerX = centerX;
  this.centerY = centerY;
}

object Circle : Shape {
  Float32 radius;
};

inline Circle( Float32 centerX, Float32 centerY, Float32 radius ) {
  this.parent.initialize( centerX, centerY );
  this.radius = radius;
}

operator entry() {
  Circle c( 1, 2, 3);
  report( c );
}

/*
** Output:

{centerX:+1.0,centerY:+2.0,radius:+3.0}

*/

デストラクタ

デストラクタは変数がスコープの外に出て、そのリソースを解放するときに呼ばれる関数です。デストラクタは型の名前の前に ~ (チルダ)をつけ、また関数として使われるように宣言されます。デストラクタはどのようなパラメーターも取らず、戻り値も返しません。デストラクタは値が解放される前に呼ばれます。したがって、そのメンバーにはまだアクセスすることができます。デストラクタの本体では、そうしたメンバーの値には this キーワードを使って参照します。また、それらの値は入出力可能なので、つまりはデストラクタで書き換え可能です。

デストラクタの使用例

struct MyType {
  String s;
};

// Empty constructor
function MyType() {
  this.s = "foo";
  report("Creating MyType: this.s = " + this.s);
}

// Destructor
function ~MyType() {
  report("Destroying MyType: this.s = " + this.s);
}

operator entry() {
  MyType myType;
}

出力:

Creating MyType: this.s = foo
Destroying MyType: this.s = foo

関数のように、デストラクタはオプションとして function の代わりに inline キーワードを使って定義できます。詳細は Inline 関数,メソッド を見てください。

特殊化された構造体またはオブジェクト型が基底型を継承するとき、基底型のデストラクタは特殊化した後に呼び出されます。

メソッド

method はユーザー定義の構造体上で操作を行う関数です。メソッドは、普通の関数呼び出しよりもわずかに異なる(そしてより示唆的な)文法を使います。これは、メソッドコールがユーザー定義の構造体を型として持つ値に強く結びつけられていることによります。

メソッド定義

もし Type が構造体かエイリアスだった場合、 methodName と名付けられたメソッドは次の文法を使うことで、型に加えることができます。

// A method that returns a value
function <ReturnType> <Type>.<methodName>(<parameter list>) {
  <method body>
}

// A method that does not return a value
function <Type>.<methodName>(<parameter list>) {
  <method body>
}

メソッド本体内で、 this はそのメソッドが呼ばれた値を参照します。this は、もしそのメソッドが値を返すのであれば、読み込みのみ可能です。もし、値を返さないのであれば、読み書きが可能です。

関数のように、メソッドはオプションとして function キーワードの代わりに inline を使って定義することができます。詳細は Inline 関数,メソッド をご覧ください。

メソッド呼び出し

もし valueType 型の値であるなら、そのメソッド methodNamevalue.methodName(argument list) という書き方を使って、 value 上で呼び出すことができます。

同名な関数を複数を置くことが可能であるので、指定されたある型は同名の複数の関数を持つことが可能です。どのメソッドを実際に呼び出すかを決定するには、通常の最適ルールが適用されます。

メソッド定義と呼び出しの例

/*
** Example: Method Definition and Invocation
*/

struct MyType {
  Integer a;
  Float32 b;
};

// Add method desc to MyType
function String MyType.desc() {
  return "a:" + this.a + "; b:" + this.b;
}

operator entry() {
  MyType t;
  t.a = 1;
  t.b = 3.14;
  // Reports 'a:1; b:3.14'
  report(t.desc());
}

/*
** Output:

a:1; b:+3.14

*/

メソッドにおける this の Read-Only・Read-Write な値としての扱い

バージョン 1.12.0 で変更: this は今後、以下の条件 ―メソッド定義においてメソッド名の後ろに明示的に ! を後置しない限り― 以外であれば常にディフォルトで read-only となります。もはやディフォルトはメソッドが値を返すかどうかに依存することはありません。

this が read-only か read-write (コンパイラ用語では, r-value あるいは l-value) かどうかは、メソッド単位で制御することができます。ディフォルトでは this は read-only; メソッド名に ! (エクスクラメーションびっくりマーク) を接尾すると read-write; メソッド名に ? (クエスチョンマーク)を接尾すると明示的な read-only となります。

メソッド内の this の明示的な read-only・read-write 使用例

/*
** Example: Explicit read-only or read-write "this" in methods
*/

struct Vec2 {
  Float64 x;
  Float64 y;
};

function Vec2(in Float64 x, in Float64 y) {
  this.x = x;
  this.y = y;
}

// Explicitly make 'this' read-only
function Vec2.getComponents?(io Float64 x, io Float64 y) {
  x = this.x;
  y = this.y;
}

function Float64 Vec2.normSq() {
  return this.x*this.x + this.y*this.y;
}

function Float64 Vec2.norm() {
  return sqrt(this.normSq());
}

function Vec2./=(in Float64 value) {
  this.x /= value;
  this.y /= value;
}

// Explicitly make 'this' read-write
function Float64 Vec2.normalizeAndReturnOldNorm!() {
  Float64 oldNorm = this.norm();
  this /= oldNorm;
  return oldNorm;
}

operator entry() {
  Vec2 vec2(3.14, 2.71);

  Float64 x, y;
  vec2.getComponents(x, y);
  report("vec2.getComponents: x=" + x + ", y=" + y);

  report("vec2.normalizeAndReturnOldNorm returned " + vec2.normalizeAndReturnOldNorm());
  report("vec2 is now " + vec2);
}

/*
** Output:

vec2.getComponents: x=+3.14, y=+2.71
vec2.normalizeAndReturnOldNorm returned +4.147734321289154
vec2 is now {x:+0.757039809392627,y:+0.653368752692363}

*/

インターフェイスメソッドと継承

バージョン 1.13.0 で追加.

KLコーダにとっては自明なことではありますが、インターフェイスメソッドの呼び出しメカニズムは通常のメソッドとは異なり、ゆえに特定の状況では特に注意を必要とします。

特殊化したオブジェクトは、 基底となるオブジェクト型 を継承することができます。この基底型がインタフェースを実装している場合、特殊化したオブジェクトではさらに同じインタフェースのメソッドを独自に実装することができます。このような場合では、インタフェースメソッドの呼び出しは常に特殊化したバージョンのメソッドが呼びだされます。(つまり特殊化したオブジェクトのメソッドが基底オブジェクトのメソッドを overrides した)これは常にこの通りになります。メソッド呼び出しが、関数コンテキストでおこなわれようと特殊化したオブジェクトのメソッドであろうと、基本オブジェクトのメソッドであろうと関係ありません。

ただし、特殊化したメソッドの実装において、基底の実装を呼び出す必要が有ることはよくあります。 Type.parent.methodName 記法により、特殊化クラスが基底のインタフェースメソッド実装を呼び出すことができます。以下のとおり:

interface Described {
  String describe();
};

object Shape : Described {
  Float32 centerX, centerY;
};

function String Shape.describe() {
  return "Center: (" + this.centerX + ", " + this.centerY + ")";
}

object Circle : Shape {
  Float32 radius;
};

inline Circle.setRadius!( Float32 r ) {
  this.radius = r;
}

function String Circle.describe() {
  // Call Shape.describe and append to it
  return this.parent.describe() + " Radius: " + this.radius;
}

operator entry() {
  Circle c();
  c.centerX = 1;
  c.centerY = 2;
  c.radius = 3;

  Described d = c;
  report( d.describe() );
}

/*
** Output:

Center: (+1.0, +2.0) Radius: +3.0

*/

メソッドへのアクセス

バージョン 1.15.0 で追加.

メソッドへのアクセスは メンバへのアクセス 同様 public, private, protected キーワードにより制御可能です:

/*
** Example: Access to Methods
*/

interface Int
{
  private int_priv();
  protected int_prot();
};

object A : Int
{
};

public A.a_pub()
{
  this.int_priv(); // ok since A implements Int
  this.int_prot(); // ok since A implements Int
}

protected A.a_prot() {}
private A.a_priv() {}

A.int_priv() {}
A.int_prot() {}

object B : A
{
};

public A.b_pub()
{
  this.int_priv(); // error since int_priv is private
  this.int_prot(); // ok since B inherits A
}

protected B.b_prot()
{
  this.a_prot(); // ok since B inherits A
}

private B.b_priv()
{
  this.a_priv(); // error since a_priv is private
}

operator entry()
{
  A a();
  a.a_pub(); // ok since a_pub is public
  a.a_prot(); // error since a_prot is protected
  a.a_priv(); // error since a_prot is private

  B b();
  b.b_pub(); // ok since a_pub is public
  b.b_prot(); // error since a_prot is protected
  b.b_priv(); // error since a_prot is private
}

/*
** Output:
(stdin):52:3: error: cannot access protected function A.a_prot?()
(stdin):53:3: error: cannot access private function A.a_priv?()
(stdin):57:3: error: cannot access protected function B.b_prot?()
(stdin):58:3: error: cannot access private function B.b_priv?()
(stdin):45:3: error: cannot access private function A.a_priv?()


*/

演算子オーバロード

KLでは独自のカスタム型(例えば struct )用に2項演算子や、複合代入演算子をオーバロードすることができます。

関数とおなじく、演算子オーバロードも inline キーワードを function の箇所に付して定義することも可能です。 Inline 関数,メソッド 参照

二項演算子のオーバロード

二項演算子のオーバロードには以下の文法に従います:

/*
** Example: Binary Operator Overloads
*/

struct MyType {
  Integer a;
  Float32 b;
};

function MyType +(MyType lhs, MyType rhs) {
  MyType result;
  result.a = lhs.a + rhs.a;
  result.b = lhs.b + rhs.b;
  return result;
}

operator entry() {
  MyType t1; t1.a = 42; t1.b = 3.14; report(t1);
  MyType t2; t2.a = 7; t2.b = 2.72; report(t2);
  MyType t3 = t1 + t2; report(t3);
}

/*
** Output:

{a:42,b:+3.14}
{a:7,b:+2.72}
{a:49,b:+5.86}

*/

どの二項演算子、算術演算子 (+, -, *, /, %), ビット演算子 (|, &, ^, << and >>) 比較演算子 (==, !=, <, <=, > and >=) もオーバロード可能です。

二項演算子のオーバロードには以下の制限があります:

  • パラメータを『2つ』とります。この2つのパラメータは、どのような型でも可能であり、互いに異なる型でもよいですが、双方 input-only のパラメータである必要があります。
  • 値を返します。ただし返り値の型は問いません。

単項演算子のオーバロード

バージョン 1.12.0 で追加: 単項演算子のオーバロード

単項演算子のオーバーロードは以下の文法に従います:

/*
** Example: Binary Operator Overloads
*/

struct MyType {
  Integer a;
  Float32 b;
};

function MyType -MyType() {
  MyType result;
  result.a = -this.a;
  result.b = -this.b;
  return result;
}

operator entry() {
  MyType t1; t1.a = 42; t1.b = 3.14; report(-t1);
  MyType t2; t2.a = 7; t2.b = 2.72; report(-t2);
}

/*
** Output:

{a:-42,b:-3.14}
{a:-7,b:-2.72}

*/

単項演算子は +, -, ~ のみオーバロード可能です。

単項演算子のオーバーロードには以下の制限があります:

  • 値を返します。ただし返り値の型は問いません。

直接代入のオーバロード

KL provides a default direct assignment for custom types which simply assigns each of the members. However, it is also possible to provide an overload for the direct assignment operator as shown in the example below:

/*
** Example: Direct Assignment Overload
*/

struct A {
  UInt32 a;
};

A(UInt32 x) {
  this.a = x;
}

A.=(A a) {
  report("Performing assignment");
  this.a = 2 * a.a;
}

operator entry() {
  A a1(42), a2(56);
  report("Before: a1 = " + a1 + ", a2 = " + a2);
  a1 = a2;
  report("After: a1 = " + a1 + ", a2 = " + a2);
}

/*
** Output:

Before: a1 = {a:42}, a2 = {a:56}
Performing assignment
After: a1 = {a:112}, a2 = {a:56}

*/

複合代入演算子のオーバロードは以下の制限に従います:

  • 『1つ』パラメータを取ります。このパラメータはどのような型でも構いません。パラメータは input-only である必要があります。
  • 値を返しません。

複合代入演算子のオーバロード

KLでは、カスタム型にディフォルトの直接代入演算子を提供します。代入の際は単純にそれぞれのメンバを代入します。 compound assignment 演算子についても同様に提供します (例 +=, -=, *=, /=, %=, |=, &=, ^=, <<=, >>=) 関連する二項演算子 ―利用可能であれば代入も― を作成します。

ただし、複号代入演算子のオーバロードをすることも可能です。以下に例を示します:

struct Type {
  Integer a;
  Float32 b;
};

function Type.+=(Type that) {
  this.a += that.a;
  this.b += that.b;
}

operator entry() {
  Type t1; t1.a = 42; t1.b = 3.14; report("t1 is " + t1);
  Type t2; t2.a = 7; t2.b = 2.72; report("t2 is " + t2);
  t1 += t2; report("t1 is now " + t1);
}

以下の出力を得ます:

t1 is {a:42,b:3.14}
t2 is {a:7,b:2.72}
t1 is now {a:49,b:5.86}

複合代入演算子のオーバロードは以下の制限に従います:

  • 『1つ』パラメータを取ります。このパラメータはどのような型でも構いません。パラメータは input-only である必要があります。
  • 値を返しません。

Inline 関数,メソッド

関数、メソッドなど(オペレータを除く)は宣言の際にオプションとして、 inline キーワードを function キーワードの箇所に付すことができます。 inline キーワードにより、KLは使用に際し関数定義を inline 化しようとします。 inline は通常、小さな関数に対してのみ使用します。これにより実行時のパフォーマンスの改善を見込めます。

inline Integer add(Integer lhs, Integer rhs) {
  return lhs + rhs;
}

組み込み関数・メソッド

KL にはいくつかの組み込み関数・メソッドがあり、KLプログラム全てで利用可能です。

デバッグのための関数

function report(String message)

メッセージを出力します。KLが送った場所へ出力します; Fabric Engine ― あるいは KL Toolコマンドラインから使用されたのであれば、出力は標準エラー・標準出力へとそれぞれ送られます。送信時に改行がメッセージに追記されます。

Fabric Engine では report関数は、主にデバッグのため使われ、対してKL Tool では Report関数は一般的な出力のために使われます。

function dumpstack()

バージョン 1.13.0 で追加.

KL関数のコールスタックを出力します。KLファイルの名前・行位番号を含む呼び出し位置を含みます。以下にKLコード例を示します。

function func2()
{
  dumpstack();
}

function func1()
{
  func2();
}

operator entry()
{
  func1();
}

以下の出力となります:

1 function.func2() call.kl:4
2 function.func1() call.kl:9
3 operator.entry() call.kl:14
4 kl.internal.entry.stub.cpu()

エラーステータスの関数

KLは、エラーステータスのコンテキストを保持します。このステータスは組み込み関数により、設定(set)・照会(query)・リセットが可能です。このステータスは、KLの評価コンテキストとスレッドに制限されます。いくつかのKLでの操作 ――たとえば整数のゼロ除算, 配列の境界外アクセス(境界チェックを有効にしKLを実行している場合)など、は内部で setError を呼びます。 Fabric Engine エクステンションは通常、操作の失敗を報告するためエラーステータスを設定します。

function String getLastError()

設定されている最終エラーステータスを取得します。

function clearLastError()

最終エラーステータスをリセットします。

function setError(String status)

新しいエラーステータスを設定し、 report メカニズムを利用しレポートします。

整数の数値演算関数

KLは、いくつかの整数関数をサポートします。整数式を扱う際に役立ちます。これら各関数には、それぞれの数値型 (UInt8, SInt8; UInt16, SInt16; UInt32, SInt32; UInt64, SInt64)のバージョンがあります。実際にどの関数が呼ばれるかは、ポリモーフィズムのベストマッチルールに従い選ばれます。 ポリモーフィズム 参照

function <SignedIntegerType> abs(<IntegerType> n)

引数の値の整数の絶対値を返します。

引数「 n 」の型によらず、返り値の型は符号付きとなり、符号付き整数として解釈される n の絶対値を返します。これによって abs 関数を、異なる符号なし整数同士を式に対して使用する(例: abs(Size(offset)-Size(index)) )ことができます。

浮動小数点数の数値演算関数

KLは、C言語由来の「標準ライブラリ」より浮動小数点数の数値演算関数を多数サポートします。どの関数も Float32 あるいは Float64 のパラメータ(複数可)をとるバージョンが存在します。実際にどの関数が呼ばれるかは、ポリモーフィズムのベストマッチルールに従い選ばれます。 ポリモーフィズム.

三角関数

C言語の標準ライブラリ同様、全ての三角関数は、引数と返り値ににラジアンを(それぞれ適切な箇所に)使用します。

function Float32 sin(Float32 x)
function Float64 sin(Float64 x)

x の角度の正弦(サイン)を返します。 x の単位はラジアンです。

function Float32 cos(Float32 x)
function Float64 cos(Float64 x)

x の角度の余弦(コサイン)を返します。 x の単位はラジアンです。

function Float32 tan(Float32 x)
function Float64 tan(Float64 x)

x の角度の正接(タンジェント)を返します。 x の単位はラジアンです。

function Float32 asin(Float32 x)
function Float64 asin(Float64 x)

x の逆正弦(アークサイン)を返します。返り値の単位はラジアンです。

function Float32 acos(Float32 x)
function Float64 acos(Float64 x)

x の逆余弦(アークコサイン)を返します。返り値の単位はラジアンです。

function Float32 atan(Float32 x)
function Float64 atan(Float64 x)

x の逆正接(アークコタンジェント)を返します。返り値の単位はラジアンです。

警告

この関数は巨大な x では機能しません。 \((-\pi/2,\pi/2)\) の範囲でのみ値を返します。範囲外のものは atan2 をかわりに使用します。
function Float32 atan2(Float32 y, Float32 x)
function Float64 atan2(Float64 y, Float64 x)

y/x の逆正接(アークタンジェント)をラジアンで返します; 返り値の範囲は \((-\pi,\pi)\) になります。

指数・対数関数

function Float32 pow(Float32 x, Float32 y)
function Float64 pow(Float64 x, Float64 y)

xy 乗の値を返します。

function Float32 pow(Float32 x, <IntegerType> y)
function Float64 pow(Float64 x, <IntegerType> y)

xy 乗の値を返します。 y は整数です。高速化のため2のべき乗を使用し、とくに y が定数な整数である場合、固定操作に展開されます。

function Float32 exp(Float32 x)
function Float64 exp(Float64 x)

\(e\)x 乗の値を返します。 \(e\) は自然対数の底です (近似値は 2.7182818...)

function Float32 log(Float32 x)
function Float64 log(Float64 x)

x の自然対数を返します(底は \(e\)

function Float32 log10(Float32 x)
function Float64 log10(Float64 x)

x の10を底とした対数(常用対数)を返します。

非超越関数

function Float32 abs(Float32 x)
function Float64 abs(Float64 x)

x の絶対値を返します。

function Float32 round(Float32 x)
function Float64 round(Float64 x)

x に、最も近い整数(ゼロの小数部)に丸めた浮動小数点数の値を返します。

function Float32 floor(Float32 x)
function Float64 floor(Float64 x)

x と等しいか、x 以下の最も大きい整数を浮動小数点数で返します。

function Float32 ceil(Float32 x)
function Float64 ceil(Float64 x)

x と等しいか、x 以上の最も小さい整数を浮動小数点数で返します。

カテゴリ関数

function Boolean Float32.isReg()
function Boolean Float64.isReg()

浮動小数点数が、通常の浮動小数点数である場合にのみ true を返します。つまり、infinite や NaN(非数)値でない場合です。

function Boolean Float32.isInf()
function Boolean Float64.isInf()

浮動小数点数が、infinite 浮動小数点数である場合にのみ true を返します。ただし NaN(非数)値チェックは行いません。必要であれば Float32.isNaN() を併用してください。

function Boolean Float32.isNaN()
function Boolean Float64.isNaN()

浮動小数点数が、非数(NaN)な浮動小数点数である場合にのみ true を返します。ただし infinite 値チェックは行いません。必要であれば Float32.isInf() を併用してください。

注釈

ある浮動小数点数 x について、 !x.isReg()x.isInf() || x.isNaN() と同等です

ベクタ関数

KLは、大規模な vector functions 一式そろえています。これらの関数は以下の構造ぶつについて自動的に使用可能となります; 構造体のメンバが ―全て同じ整数型である,あるいは全て同じ浮動小数点数型である;(つまりベクタを表現する構造体が通常そうであるように)KLコンパイラは、実行中のアーキテクチャに最適なベクタ固有の操作となるよう、呼び出す関数を削減します。たとえば現代の Intel x86マシン上であれば、SSEまたはAVXのベクタ拡張を使用し命令(instruction)を削減します。これにより非ベクタコードを凌ぐパフォーマンスの改善を見込めます。

<V> が構造体であり、メンバ <m1>, <m2>, ... <mN> が全て同一の(整数、浮動小数点数の)型 <T> であるなら、以下の関数が利用可能です:

function <V> vecAdd(<V> lhs, <V> rhs)

lhs.m1 + rhs.m1, lhs.m2 + rhs.m2, ... lhs.mN + rhs.mN を返します

function <V> vecAdd(<T> k, <V> rhs)

k + rhs.m1, k + rhs.m2, ... k + rhs.mN を返します

function <V> vecAdd(<V> lhs, <T> k)

lhs.m1 + k, lhs.m2 + k, ... lhs.mN + k を返します

function <V> vecSub(<V> lhs, <V> rhs)

lhs.m1 - rhs.m1, lhs.m2 - rhs.m2, ... lhs.mN - rhs.mN を返します

function <V> vecSub(<T> k, <V> rhs)

k - rhs.m1, k - rhs.m2, ... k - rhs.mN を返します

function <V> vecSub(<V> lhs, <T> k)

lhs.m1 - k, lhs.m2 - k, ... lhs.mN - k を返します

function <V> vecMul(<V> lhs, <V> rhs)

lhs.m1 * rhs.m1, lhs.m2 * rhs.m2, ... lhs.mN * rhs.mN を返します

function <V> vecMul(<T> k, <V> rhs)

k * rhs.m1, k * rhs.m2, ... k * rhs.mN を返します

function <V> vecMul(<V> lhs, <T> k)

lhs.m1 * k, lhs.m2 * k, ... lhs.mN * k を返します

function <V> vecDiv(<V> lhs, <V> rhs)

lhs.m1 / rhs.m1, lhs.m2 / rhs.m2, ... lhs.mN / rhs.mN を返します

function <V> vecDiv(<T> k, <V> rhs)

k / rhs.m1, k / rhs.m2, ... k / rhs.mN を返します

function <V> vecDiv(<V> lhs, <T> k)

lhs.m1 / k, lhs.m2 / k, ... lhs.mN / k を返します

function <V> vecRem(<V> lhs, <V> rhs)

lhs.m1 % rhs.m1, lhs.m2 % rhs.m2, ... lhs.mN % rhs.mN を返します

function <V> vecRem(<T> k, <V> rhs)

k % rhs.m1, k % rhs.m2, ... k % rhs.mN を返します

function <V> vecRem(<V> lhs, <T> k)

lhs.m1 % k, lhs.m2 % k, ... lhs.mN % k を返します

<T> が整数型であれば、更に追加で以下の関数が利用可能です。

function <V> vecBitOr(<V> lhs, <V> rhs)

lhs.m1 | rhs.m1, lhs.m2 | rhs.m2, ... lhs.mN | rhs.mN を返します

function <V> vecBitOr(<T> k, <V> rhs)

k | rhs.m1, k | rhs.m2, ... k | rhs.mN を返します

function <V> vecBitOr(<V> lhs, <T> k)

lhs.m1 | k, lhs.m2 | k, ... lhs.mN | k を返します

function <V> vecBitAnd(<V> lhs, <V> rhs)

lhs.m1 & rhs.m1, lhs.m2 & rhs.m2, ... lhs.mN & rhs.mN を返します

function <V> vecBitAnd(<T> k, <V> rhs)

k & rhs.m1, k & rhs.m2, ... k & rhs.mN を返します

function <V> vecBitAnd(<V> lhs, <T> k)

lhs.m1 & k, lhs.m2 & k, ... lhs.mN & k を返します

function <V> vecBitXor(<V> lhs, <V> rhs)

Returns lhs.m1 ^ rhs.m1, lhs.m2 ^ rhs.m2, ... lhs.mN ^ rhs.mN

function <V> vecBitXor(<T> k, <V> rhs)

k ^ rhs.m1, k ^ rhs.m2, ... k ^ rhs.mN を返します

function <V> vecBitXor(<V> lhs, <T> k)

lhs.m1 ^ k, lhs.m2 ^ k, ... lhs.mN ^ k を返します

function <V> vecShl(<V> lhs, <V> rhs)

lhs.m1 << rhs.m1, lhs.m2 << rhs.m2, ... lhs.mN << rhs.mN を返します

function <V> vecShl(<T> k, <V> rhs)

k << rhs.m1, k << rhs.m2, ... k << rhs.mN を返します

function <V> vecShl(<V> lhs, <T> k)

Returns lhs.m1 << k, lhs.m2 << k, ... lhs.mN << k を返します

function <V> vecShr(<V> lhs, <V> rhs)

lhs.m1 >> rhs.m1, lhs.m2 >> rhs.m2, ... lhs.mN >> rhs.mN を返します

function <V> vecShr(<T> k, <V> rhs)

k >> rhs.m1, k >> rhs.m2, ... k >> rhs.mN を返します

function <V> vecShr(<V> lhs, <T> k)

lhs.m1 >> k, lhs.m2 >> k, ... lhs.mN >> k を返します

変換関数

function <Type>.appendDesc(io String string)

バージョン 1.12.0 で追加.

appendDesc メソッドを与えられた型から String へと変換するために呼びます。独自の appendDesc メソッドを記述し、この変換をカスタマイズすることができます。以下に例示します:

/*
** Example: Custom appendDesc Method
*/

struct Vec3 { Float32 x, y, z; };

function Vec3(Float32 x, Float32 y, Float32 z) {
  this.x = x; this.y = y; this.z = z;
}

function Vec3.appendDesc(io String string) {
  string += "vec3:[";
  string += this.x;
  string += ":";
  string += this.y;
  string += ":";
  string += this.z;
  string += "]";
}

operator entry() {
  Vec3 vec3(6.7, -9.4, 2.3);
  report(vec3);
}

/*
** Output:

vec3:[+6.7:-9.4:+2.3]

*/
function String hex(UInt8 n)
function String hex(UInt16 n)
function String hex(UInt32 n)
function String hex(UInt64 n)

符号なし整数を16進数文字列値の表現へと変換します。

function String hex(SInt8 n)
function String hex(SInt16 n)
function String hex(SInt32 n)
function String hex(SInt64 n)

整数を16進数文字列値の表現へと変換します。 n が対応する符号なし整数であるかのように出力されます。つまり負の値は考慮されません。

function Float32 bitcastUIntToFloat(UInt32 n)
function Float64 bitcastUIntToFloat(UInt64 n)

符号なし整数を同じ幅をもつ浮動小数点数へとビットキャストします。これはKL自体のユニットテストを行う際とても有用な、非数値変換です。

function UInt32 bitcastFloatToUInt(Float32 x)
function UInt64 bitcastFloatToUInt(Float64 x)

浮動小数点数を同じ幅をもつ符号なし整数へとビットキャストします。これはKL自体のユニットテストを行う際とても有用な、非数値変換です。

パフォーマンス計測関数

KLは、高性能ななシステムタイマ情報へアクセスを提供し、KLコード中から、操作に要する時間の情報についてアクセスすることができます。

function UInt64 getCurrentTicks()

パフォーマンスカウンタの現在の値を返します。この値はそれ自体に意味があるわけでありません(単位はundefined)。ですが getSecondsBetweenTicks() の呼び出す中に使用することで、経過時間の絶対時間で計測します。 getCurrentTicks() で返る値は、システム時計(壁時計)の変更の影響を受けません。

function Float64 getSecondsBetweenTicks(UInt64 start, UInt64 end)

2つのパフォーマンスカウンタ間の値を「秒」で返します。計測解像度は、少なくとも100万分の1秒であることが保証されます。

パフォーマンス計測関数の使用例:

operator entry() {
  UInt64 start = getCurrentTicks();
  // Do nothing...
  UInt64 end = getCurrentTicks();
  report("Elapsed time: " + getSecondsBetweenTicks(start, end) + " seconds");
}

出力:

Elapsed time: 4.1e-08 seconds

Memory Usage Functions

function UInt64 klHeapInUse()

Returns the number of bytes currently allocated on the KL heap. Memory is allocated on the KL heap in order to provide memory for variable arrays, dictionaries, objects, most strings, and some other less-commonly used types.

注釈

This function cannot be called on the GPU

/*
** Example: klHeapInUse()
*/

operator entry() {
  report("klHeapInUse() before: " + klHeapInUse());
  Float32 a[](16); // allocates some memory on the KL heap
  report("klHeapInUse() after: " + klHeapInUse());
}

/*
** Output:

klHeapInUse() before: 0
klHeapInUse() after: 104

*/

Fabric Context 関数

Fabric Core コンテキストとやり取りするための関数です。

function String fabricCoreContextID()

Fabric Core コンテキストIDを文字列で返します。このコンテキストIDは、新規のFabric Coreクライアントと、既存のコンテキストをバインドするため使用します。

名前付き定数

KLにおいて named constant とは、式中より名前により参照されるある値のことであり、実行時に変更することができない値です。名前付き定数は、基本的に read-only な変数です;しかし、KLコンパイラにとってその値が不変であることがわかっているため、ただの変数のかわりに名前付き定数を使用すると、高速なコードとなります。スカラ、あるいは配列の名前付き定数を宣言することができます。

名前付き定数はどのスコープ中(グローバルスコープも含む)にも宣言が可能です。(スコープについては Scoping Rules 参照)名前付き定数は、宣言されたスコープ中からのみ可視となります。

スカラの名前付き定数は以下の形態をとります:

const Type name = expr;

配列の名前付き定数は以下の形式を取ります:

const Type name[] = [
  expr1, expr2, ..., exprN
];

どちらのケースも、 Type は boolean, integer, floating-point, string いずれかの型である; name は識別子; expr{Type} 型の定数として評価される定数を含む式である必要があります。スカラの名前付き定数の場合、その型は Type です。配列の名前付き定数の場合、その型は Type 型の要素の固定長配列です; この配列のサイズは、カッコ内に与えられた初期化する値の数と同一になります。

以下のどれかをするとコンパイル時にエラーとなります。

  • 名前付き定数への代入
  • 関数の io パラメータとして名前付き定数を渡す
  • 既存の関数(あるいはオペレータ、他の名前付き定数)と同名でグローバル名前付き定数を宣言
  • 同一スコープ内の、既存の変数(あるいは他の名前付き定数)と同名で非グローバルな名前付き定数を宣言

名前付き定数の例:

const String MODULE_NAME = "KL";
const String PREFIX = MODULE_NAME + ": ";
const UInt32 twoToTheSixteen = 1 << 16;
const Float32 familiarValues[] = [3.141, 2.718, (3 * 7.4) / 3.4];

operator entry() {
  report(PREFIX + "twoToTheSixteen = " + twoToTheSixteen);
  report(PREFIX + "familiarValues = " + familiarValues);
  for (UInt32 i=0; i<4; ++i) {
    const UInt32 a = 3;
    const UInt32 b = 4;
    report(PREFIX + "a*"+i+"+b = "+(a*i+b));
  }
}

出力:

KL: twoToTheSixteen = 65536
KL: familiarValues = [+3.141000,+2.717999,+6.529411]
KL: a*0+b = 4
KL: a*1+b = 7
KL: a*2+b = 10
KL: a*3+b = 13

事前定義された定数

全KLプログラムで利用可能な種々の事前定義された定数があります。

Fabric バージョン Pre-Defined 定数

The three constants FabricVersionMaj, FabricVersionMin and FabricVersionRev are three predefined constants of type UInt8 that are the major, minor and revision components of the running Fabric version. For example, this documentation was built for Fabric version 2.4.0, and so KL code executed in this version will have FabricVersionMaj = |FABRIC_VERSION_MAJ|, FabricVersionMin = |FABRIC_VERSION_MIN| and FabricVersionRev = |FABRIC_VERSION_REV|.

operator entry() {
  report("FabricVersionMaj = " + FabricVersionMaj);
  report("FabricVersionMin = " + FabricVersionMin);
  report("FabricVersionRev = " + FabricVersionRev);
}

整数限界の事前定義定数

全ての整数型 <IntTy> には事前定義された整数定数 <IntTy>Max があり、整数が許容する最大値を示します。符号付き整数では追加で <IntTy>Min があり、整数の許容する最小値を示します。どちらでも整数定数の型はその整数自身の型です。例:

operator entry() {
  report("UInt8Max=" + UInt8Max);
  report("UInt16Max=" + UInt16Max);
  report("UInt32Max=" + UInt32Max);
  report("UInt64Max=" + UInt64Max);
  report("SInt8Min=" + SInt8Min);
  report("SInt8Max=" + SInt8Max);
  report("SInt16Min=" + SInt16Min);
  report("SInt16Max=" + SInt16Max);
  report("SInt32Min=" + SInt32Min);
  report("SInt32Max=" + SInt32Max);
  report("SInt64Min=" + SInt64Min);
  report("SInt64Max=" + SInt64Max);
}

出力結果

UInt8Max=255
UInt16Max=65535
UInt32Max=4294967295
UInt64Max=18446744073709551615
SInt8Min=-128
SInt8Max=127
SInt16Min=-32768
SInt16Max=32767
SInt32Min=-2147483648
SInt32Max=2147483647
SInt64Min=-9223372036854775808
SInt64Max=9223372036854775807

FUNC 事前定義定数

KLコンパイラは、関数のスタート時その関数を説明する文字列定数として、自動的に FUNC を事前定義します。以下のコードにしたがいます:

function foo(Float32 x) {
  report("This function is: " + FUNC);
}

operator entry() {
  foo(3.14);
}

出力結果

This function is: function foo(Float32)

require による機能のインポート

Fabric との統合により、――たとえばKLの型と(もしくは) Fabricエクステンションを継承するため―― 外部に定義されたKLコードを現在のソースコードへと提供することが可能となります。これらの型、コードを現在のソースファイルで使用するには、 require 文を使用します; Pythonにおける import 文と似ています。

require` 文の後には、登録された型(registered type)、エクステンションの名前を続けます。例えば、 “Math” という名のエクステンションと “RegType” という名の登録された方に提供されているの機能を含めたいのであれば、このようにプログラムをはじめます:

require Math, RegType;

どの require 文も 関連する機能をつかうKLプログラム中、最上部に記載します。複数の require 文を望みの数記述することもできます。

バージョン情報と共に require を使用する

ディフォルトで require 文は、エクステンションの利用可能な最新バージョンのを読み込みに行きます。 ExtensionName エクステンションに2つのバージョン "1.0.0""1.2.1" があるとします、

require ExtensionName;

とすると、 "1.2.1" バージョンのものが読み込まれる結果となります。もし特定バージョンのものを読み込みたいのであれば、以下のシンタックスにより

require ExtensionName:"=1.0.0";

指定した "1.0.0" バージョンが読み込まれます。そのバージョンが存在しない場合、エラーが投げられます。あるいは、エクステンションが指定バージョン以上であることが望みであれば、大なり小なり不等号記号を使用し、以下のようにします:

require ExtensionName:">1.0.0";

指定のバージョン以下、(以上)が存在しない場合は、やはりエラーが投げられます。この例ではバージョン "1.2.1" が読み込まれます。

さらに、プリプロセッサ文を使用しオプションのKLコードを追加したり、あるいは、エクステンションのバージョンに基づいた振舞のスイッチをさせたりすることができます。 EXT_VER_IF: :code:`EXT_VER_ENDIF 文を使用し、等号、大なり、小なり記号を利用できます。

require ExtensionName;

EXT_VER_IF ExtensionName:">1.0.0"

function dummyFunction() {
  report('Found an extension version for "ExtensionName" higher than "1.0.0"');
}

EXT_VER_ENDIF

operator entry() {

EXT_VER_IF ExtensionName:">1.0.0"

  dummyFunction();

EXT_VER_ENDIF

}

dummy 関数の定義と呼び出しは、エクステンション ExtensionName のバージョンが "1.0.0" 以上の場合にのみ、上記の例では発生します。

If you wish to check the version of Fabric itself, use Fabric as the extension name:

/*
** Example: Conditional Compilation using Preprocessor Statements
*/

EXT_VER_IF Fabric : "< 2.0.0"
foo() {
  report("Version is < 2.0.0");
}
EXT_VER_ENDIF

EXT_VER_IF Fabric : ">= 2.0.0"
foo() {
  report("Version is >= 2.0.0");
}
EXT_VER_ENDIF

operator entry() {
  foo();
}

/*
** Output:

Version is >= 2.0.0

*/

バージョン情報をエクステンションに埋め込むための、より詳しい情報については version :エクステンション・バージョニングの指定(任意) をご参照ください。

エクステンションのバージョニング環境変数

上述の バージョン情報と共に require を使用する の機能につけくわえ、環境変数の設定によっても require 文を駆動することができます。環境変数を使用するにはいくつか方法があります。

1番目のアプローチは、各エクステンションに対し単一の環境変数を使用します。環境変数の定義は次のようにします:

export FABRIC_EXT_VER_EXTENSIONNAME="=1.0.0"

2番めのアプローチは、とくに、与えられたあるビルドセットに対し大量の環境変数を切り替える必要があるような場合、とてもぴったりです。(オプション)はじめに FABRIC_EXT_VER_PREFIXFABRIC_EXT_VER_SUFFIX 環境変数を指定します; この変数に、追加の環境変数を検索する際に使用される、接頭語と接尾語を設定します。指定しない場合はディフォルト値が使用されます。

つぎに、接頭語と接尾語とエクステンション名を使用し、各エクステンションに対し環境変数 ―結果としてバージョン情報含むことになる― をそれぞれ指定します。ディフォルトの接頭語と接尾語を用いた例:

export FABRIC_EXT_VER_EXTENSIONNAME="=1.0.0"

接頭語と接尾語を利用するようにした例:

export FABRIC_EXT_VER_PREFIX="COMPANY_"
export FABRIC_EXT_VER_SUFFIX="_VER_INFO"
export COMPANY_EXTENSIONNAME_VER_INFO="=1.0.0"

これは、エクステンションの読み込みメカニズム中、以下のように解決されます。はじめに FABRIC_EXT_VER_PREFIXFABRIC_EXT_VER_SUFFIX 環境変数を解決します。次に以上にもとづく名前をもつ COMPANY_EXTENSIONNAME_VER_INFO 変数によって、利用可能な2つのバージョンのうち 1.0.0 が使われるべきであるとして解決します。

3番めのアプローチは、エクステンション名と使用するバージョン間のマッピングを提供する、補助 json ファイルを利用する方法です。この jsonファイルのパスは、環境変数 FABRIC_EXT_VERFILE で指定する必要があります。このファイルの内容は、以下のようにします:

{
  "Alembic": ">=1.0",
  "FBX": ">=1.1"
}

4番め(最後)のアプローチは、 FABRIC_EXT_OVERRIDE 環境変数を設定します。この変数は、「必ず一緒に読み込まれるエクステンションのセット」を指定するために使用します。各エクステンションにオーバーライドキーを定義(version :エクステンション・バージョニングの指定(任意) 参照)します。キーが FABRIC_EXT_OVERRIDE の値と一致すると、そのエクステンションが、他のエクステンション(あるいはオーバーライドキーがないもの)よりも先に読み込まれます。これにより、より低位のバージョンナンバーを持つエクステンションが読み込まれることになります。以下例:

export FABRIC_EXT_OVERRIDE="MyOverride"

オーバーライドキー “MyOverride” を持つエクステンション全てを、異なるオーバーライドキーをもつ、同じエクステンションの異なるバージョンよりも優先する結果となります。

注釈

全ての環境変数はすべて大文字を使用します。

バージョン情報をエクステンションに埋め込むための、より詳しい情報については version :エクステンション・バージョニングの指定(任意) をご参照ください。