kl2edk ユーティリティ

Fabric Engine version 1.11.0の一部として kl2edk と呼ぶユーティリティが登場しました。エクステンション開発に非常に役立ちます。エクステンションで必要となるKLの型や関数プロトタイプをC++のものへと自動で変換します。

サンプルエクステンションに付属のビルドスクリプトをカスタムエクステンション作成の土台としたのであれば、 kl2edk がビルドプロセスの一環として自動で実行されます。もし異なるビルドシステムを使用する必要があれば、 kl2edk の呼び出しをビルドプロセスに組み込みましょう。 kl2edk の使い方は、簡単です。コマンドプロンプトから kl2edk --help と実行すると詳しい用法が表示されます。

必要なビルドフラグ

サンプルエクステンションに付属の SConstript ファイルをカスタムエクステンション作成の土台としたのであれば、必要となるビルドフラグはすでにそのファイルに設定されています。独自のビルド環境(Linux、あるいはOSXで)を設定中であれば、確実に -fno-omit-frame-pointer フラグをコンパイラにわたしましょう。フレームポインタの省略(frame pointer ommision)とは、最適化の一種で、KLスタックトレースの生成が作動(実行時のエラーの際にKLスタックトレース表示する出力ための機能)するためにはこれを無効にします。このフラグの追加を忘れてしまうと、作成しているエクステンションの C++部分での KLエラーでクラッシュを引き起こします。このフラグの使用の必要性は将来のバージョンで取り除かれる予定であります。

生成されるヘッダファイル

kl2edk によって KLソースファイルから生成されたヘッダファイルは、ディフォルトでは3つのセクションからなります。

一番目のセクションにはベース FabricEDK.h ファイル ――ヘッダファイルの残りやエクステンション開発で一般的に必要となる基本型やマクロを提供 が含まれます。 FabricEDK.h については 後段 で詳述します。

二番目のセクションでは、KLから参照される可能性のある全KL構造体、オブジェクト、インタフェースの C++バージョンを提供します。これについては Types and Extensions により詳しい情報があります。

三番目のセクションでは、エクステンションからネイティブコード中に実装されることが期待されている関数のC++プロタイプがあります。これは正確には、KL関数定義中に現れる = "FunctionSymbolNane"; を含む関数です。関数のものと厳密に一致するシグネチャをもたない場合、このプロトタイプによりC++エクステンションコードでコンパイルエラーが発生します。これによりKLコードと、それが呼び出すC++関数が正しく繋がっているか確認する助けになります。

Cppコードの生成

kl2edk ではエクステンションのメインC++ファイルどうよう、関数の実装も生成できます。C++の型と、KLの型の間の変換を自動生成するには、コード生成に際してさらに一つ追加の json ファイルを加え kl2edk を支援します。このファイルは .codegen.json と末尾に付け、戦闘にはエクステンションの .fpm.json ファイルの名前と同じにします。

注釈

codegen json ファイル必須ではありません。ない場合 kl2edk は、関数本体を空にしてcppファイルを生成します。

kl2edk will attempt to generate bodies for your C++ methods based on the provided codegen json file. Please see the json example below for more information. Any incomplete cpp function will contain a warning pragma, which will allow you to see the missing implementations at compile time. To enable cpp code generation you need to specify the output folder for the cpp files with the -c command line flag like so:

kl2edk test.fpm.json -o /tmp/test/h -c /tmp/test/cpp

This will put all of the generated headers into the folder /temp/test/h and the the generated implementation files in the corresponding folder.

The code generation relies on conversion functions to be provided. The conversion functions have to use the following notation:

inline bool StringFromKL(const KL::String & from, std::string & to) {
  to = from.data();
  return true;
}

inline bool StringToKL(const std::string & from, KL::String & to) {
  to = from.c_str();
  return true;
}

The sample below will illustrate a simple case. Note that the example is trivial and not a production relevant scenario. printf of course provides redundant functionality which is already covered by KL’s report function.

KL file content:

function String String.substr(UInt32 start, UInt32 length) = "String_substr";
function String.printf() = "String_printf";

codegen.json file content:

{
  "header": "
// my extension includes
#include \"custom_conversion.h\"
#include <thirdparty_api.h>
  ",
  "functionentry": "FUNCTION_ENTRY",
  "functionexit": "FUNCTION_EXIT",
  "functionexitreturn": "FUNCTION_EXIT_RETURN",
  "parameterprefix": "local",
  "typemapping": {
    "UInt32": {
      "ctype": "unsigned int",
      "from": "uintFromKL",
      "to": "uintToKL",
      "methodop": ".",
      "defaultvalue": "0"
    },
    "String": {
      "ctype": "std::string",
      "from": "StringFromKL",
      "to": "StringToKL",
      "methodop": ".",
      "defaultvalue": "std::string()"
    }
  },
  "functionbodies": {
    "String_printf": "
  printf(\"My custom printf '%s'!\\n\", localThis_.c_str());
    "
  }
}

Resulting generated c++ code:

FABRIC_EXT_EXPORT void String_substr(
  Fabric::EDK::KL::Traits< Fabric::EDK::KL::String >::Result _result,
  Fabric::EDK::KL::Traits< Fabric::EDK::KL::String >::INParam this_,
  Fabric::EDK::KL::Traits< Fabric::EDK::KL::UInt32 >::INParam start,
  Fabric::EDK::KL::Traits< Fabric::EDK::KL::UInt32 >::INParam length
)
{
  FUNCTION_ENTRY("String_substr")

  std::string localThis_ = std::string();
  if(!StringFromKL(this_, localThis_)){
    setError("Error in String_substr. unable to convert: this_");
    return;
  }
  unsigned int localStart = 0;
  if(!uintFromKL(start, localStart)){
    setError("Error in String_substr. unable to convert: start");
    return;
  }
  unsigned int localLength = 0;
  if(!uintFromKL(length, localLength)){
    setError("Error in String_substr. unable to convert: length");
    return;
  }
  std::string local_result = localThis_.substr(localStart, localLength);
  StringToKL(local_result, _result);

  FUNCTION_EXIT("String_substr")
}

FABRIC_EXT_EXPORT void String_printf(
  Fabric::EDK::KL::Traits< Fabric::EDK::KL::String >::IOParam this_
)
{
  FUNCTION_ENTRY("String_printf")

  std::string localThis_ = std::string();
  if(!StringFromKL(this_, localThis_)){
    setError("Error in String_printf. unable to convert: this_");
    return;
  }

  printf("My custom printf '%s'!\n", localThis_.c_str());

  FUNCTION_EXIT("String_printf")
}

注釈

kl2edk will never overwrite existing cpp files, but instead write files suffixed with .stubs.

The content of the codegen file in a little more detail:

  • header: a string (can be multi-line) which gets inserted into every generated cpp right after the standard includes. this can be useful to include thirdparty api headers etc. This should also provide additional includes such as the conversion functions.
  • functionentry: a macro which is called on every function entry. This parameter is optional, so leave this field empty if you do not need to bracket your functions with entry/exit statements. The notation of the macro should be (the name ENTRYMACRO of course is up to you). ENTRYMACRO(symbolname).
  • functionexit: a macro which is called on every function exit for void functions. This parameter is optional, so leave this field empty if you do not need to bracket your functions with entry/exit statements. The notation of the macro should be: EXITMACRO(symbolname)
  • functionexitreturn: a macro which is called on every function exit for functions with return values. this is optional. The notation of the macro should be: EXITRETURNMACRO(symbolname, defaultValue)
  • parameterprefix: A prefix used for the native data type parameter names. This is parameter mandatory and must be specified.
  • typemapping: A dictionary providing information for all type conversions. The key of the dictionary contains the KL type. The flags in the dictionary are:
  • ctype: the native type to be converted to
  • from: the function to be called to perform the from conversion, from KL type to native type. the notation of both the to and from conversion functions has to be bool conversion(const fromType & from, toType & to); The conversion functions need to return true or false to flag the conversion as successful or not. the conversion functions can also call on Fabric::EDK::setError or raise an exception, for example, to provide more information about an unsuccessful conversion.
  • to: the function to be called to perform the to conversion, from native to KL type.
  • defaultvalue: the default value to be used for the type for non void functions.
  • methodop: an operator to use when accessing methods on the native type. this can be ”.” or “->”.
  • functionbodies: Optionally you can specify a dictionary here of function body implementations. This doesn’t not have to include the conversions, these will be added automatically by kl2edk. You only need to worry about the third-party api specific implementation code. As seen in the json example above, the callback String_printf will be generated with the additional lines of C++ code provided.
  • parameterconversionstoskip: Optionally you can specify an array here of strings containing a combination of a c++ symbol name and a parameter. Each symbol and parameter will be skipped for the code generation when creating the parameter conversion code. For example:
"parameterconversionstoskip": [
  "MyClass_init.this_"
]
  • methodmapping: Additionally you can specify a dictionary containing a string map from c++ symbolname to class method name. This can be important when you have multiple implementations of the same method name in KL, since the c++ symbolnames have to be different. Example:
"methodmapping": {
  "MyClass_set_variant1": "set",
  "MyClass_set_variant2": "set"
}

Namespacing

All macros provided by FabricEDK.h, with the exception of IMPLEMENT_FABRIC_EDK_ENTRIES (below), begin with FABRIC_EDK_.... All C++ types and functions provided by FabricEDK.h as well as the header file generated by kl2edk are defined in the Fabric::EDK namespace. Both of this are to avoid conflicts with third-party libraries.

For convenience, you can use the use namespace functionality of C++ to avoid prefixing the types and functions:

using namespace Fabric::EDK;

However, we recommend manually prefixing types and functions so that it is easier to find them in the case of a future API change.

The FabricEDK.h Header File

The C++ source code for an extension must always begin with the lines:

#include "MyExtensionName.h"

IMPLEMENT_FABRIC_EDK_ENTRIES( MyExtensionName )

The first line includes the header file generated by kl2edk, which in turn includes FabricEDK.h. The second line implements some internal functions that allow the Fabric Engine core to initialize the extension.

In addition to providing a definition for the IMPLEMENT_FABRIC_EDK_ENTRIES macro, the FabricEDK.h header file provides definitions for built-in KL types (see Types and Extensions), several macros required for definitions of functions and custom types, and several debugging and error-handling functions.

Build Macros

FabricEDK.h provides several flags that can be used to detect various aspects of the system on which the extension is being built. Each of these should be tested using the #ifdef C++ preprocessor directive.

FABRIC_EDK_PLATFORM_POSIX
The current system is a *NIX-type system; defined on Linux and Mac OS X.
FABRIC_EDK_PLATFORM_WINDOWS
The current system is a Windows system; defined on Windows
FABRIC_EDK_OS_LINUX
The current system is running some version of Linux
FABRIC_EDK_OS_DARWIN
The current system is running some version of Mac OS X
FABRIC_EDK_OS_WINDOWS
The current system is running some version of Windows
FABRIC_EDK_ARCH_64BIT
The current system is running a 64-bit operating system
FABRIC_EDK_ARCH_32BIT
The current system is running a 32-bit operating system
FABRIC_EDK_BUILD_RELEASE
The current build is a release build with optimizations enabled and debug symbols disabled
FABRIC_EDK_BUILD_DEBUG
The current build is a debug build with optimizations disabled and with debug symbols enabled

The FABRIC_EXT_EXPORT Function Definition Prefix

FabricEDK.h provides the single function definition macro FABRIC_EXT_EXPORT that should prefix any function definition that should be made available in KL. For example:

FABRIC_EXT_EXPORT void MyFunctionToExport(
  // ...
  )
{
  // ...
}

If you use the kl2edk utility to generate the header file for your extension and then copy the function prototypes for your function implementations then they will already use FABRIC_EXT_EXPORT.

FABRIC_EXT_EXPORT does two things: it guarantees that the symbol is visible (“exported”) in the native code shared library, and it disables “mangling” of the symbol name so that the name of the exported symbol is exactly the function name.

注釈

Only symbols that will be called from KL need the FABRIC_EXT_EXPORT prefix; other functions and methods in the native code source file can be left without the prefix.

Debugging and Error-Handling Functions

FabricEDK.h provides several functions and macros that are used for debugging and error handling.

void Fabric::EDK::report(char const *format, ...)
void Fabric::EDK::report(uint32_t length, char const *data)

The equivalent of the KL report function. The first variation takes a printf-style format string and a list of arguments, while the second takes a string length and a pointer to raw character data.

void Fabric::EDK::log(char const *format, ...)
void Fabric::EDK::log(uint32_t length, char const *data)

Aliases for Fabric::EDK::report.

char const *Fabric::EDK::getExtensionName()

Returns a C-style null-terminated string that is the name of the extension.

void Fabric::EDK::setError(char const *cStrFormat, ...)
void Fabric::EDK::setError(uint32_t length, char const *data)
void Fabric::EDK::setError(std::string const &string)

The equivalent of the KL setError function. The first variation takes a printf-style format string and a list of arguments, the second takes a string length and a pointer to raw character data, and the third takes an STL std::string.

void Fabric::EDK::throwException(char const *cStrFormat, ...)
void Fabric::EDK::throwException(uint32_t length, char const *data)
void Fabric::EDK::throwException(std::string const &string)

The equivalent of the KL throw statement. The first variation takes a printf-style format string and a list of arguments, the second takes a string length and a pointer to raw character data, and the third takes an STL std::string.

注釈

You must use the Fabric::EDK::throwException function to throw an exception from within an extension; using the C++ throw keyword will not work.

Older Extensions (before kl2edk)

バージョン 1.13.0 で追加.

Some users may have older extensions which are not yet using kl2edk. For these users there is an additional #define which must be declared before calling IMPLEMENT_FABRIC_EDK_ENTRIES. An example of this define can be seen here:

#define FABRIC_EDK_EXT_MyExtensionName_DEPENDENT_EXTS \
  { \
    { "MyDependentExt", 1, 5, 2, NULL }, \
    { 0, 0, 0, 0, 0 } \
  }

This #define specifies the version of extensions on which the current one depends as a static array terminated by an entry which is all zeros. In the above example the MyExtensionName extension depends on version 1.5.2 of the MyDependentExt extension. When matching extensions Fabric will match any extension in the minor version range greater than or equal to the specified version, so in this example MyDependentExt >= 1.5.2 and < 1.6.0 will match.

The values passed in each row of the DEPENDENT_EXTS define are: