Calling C and Fortran Code

ほとんどのコードはJuliaで書くことができますが、すでにCやFortranで書かれた高品質で成熟した数値計算用のライブラリが多数存在します。この既存のコードを簡単に利用できるように、JuliaはCおよびFortran関数を呼び出すのを簡単かつ効率的にします。Juliaには「ボイラープレートなし」の哲学があります:関数は「グルー」コード、コード生成、またはコンパイルなしで、直接Juliaから呼び出すことができます - インタラクティブプロンプトからでも同様です。これは、適切な呼び出しを@ccallマクロ(または、あまり便利ではないccall構文)を使うことで実現されます。詳細はccall syntax sectionを参照してください。

呼び出されるコードは共有ライブラリとして利用可能でなければなりません。ほとんどのCおよびFortranライブラリは、すでに共有ライブラリとしてコンパイルされて出荷されますが、GCC(またはClang)を使用して自分でコードをコンパイルする場合は、-sharedおよび-fPICオプションを使用する必要があります。JuliaのJITによって生成される機械命令は、ネイティブC呼び出しと同じであるため、結果としてのオーバーヘッドはCコードからライブラリ関数を呼び出すのと同じです。 [1]

デフォルトでは、Fortran コンパイラは generate mangled names (例えば、関数名を小文字または大文字に変換し、しばしばアンダースコアを追加します)を使用します。そのため、Fortran 関数を呼び出すには、使用している Fortran コンパイラによって従われるルールに対応するマングルされた識別子を渡す必要があります。また、Fortran 関数を呼び出す際には、すべての入力をヒープまたはスタック上の割り当てられた値へのポインタとして渡す必要があります。これは、通常ヒープに割り当てられる配列や他の可変オブジェクトだけでなく、通常スタックに割り当てられ、C や Julia の呼び出し規約を使用する際に一般的にレジスタに渡される整数や浮動小数点数などのスカラー値にも適用されます。

@ccallを使用してライブラリ関数を呼び出すための構文は次のとおりです:

  @ccall library.function_name(argvalue1::argtype1, ...)::returntype
  @ccall function_name(argvalue1::argtype1, ...)::returntype
  @ccall $function_pointer(argvalue1::argtype1, ...)::returntype

ここで library は文字列定数またはリテラルです(ただし、以下の Non-constant Function Specifications を参照してください)。ライブラリは省略可能で、その場合、関数名は現在のプロセスで解決されます。この形式は、Cライブラリ関数、Juliaランタイム内の関数、またはJuliaにリンクされたアプリケーション内の関数を呼び出すために使用できます。ライブラリへのフルパスも指定できます。あるいは、@ccall を使用して、Libdl.dlsym によって返されるような関数ポインタ $function_pointer を呼び出すこともできます。argtype はC関数のシグネチャに対応し、argvalue は関数に渡される実際の引数値です。

Note

以下は、map C types to Julia typesの方法です。

以下は、ほとんどのUnix系システムの標準Cライブラリからclock関数を呼び出す完全でシンプルな例です:

julia> t = @ccall clock()::Int32
2292761

julia> typeof(t)
Int32

clock は引数を取らず、Int32 を返します。環境変数の値へのポインタを取得するために getenv 関数を呼び出すには、次のように呼び出します:

julia> path = @ccall getenv("SHELL"::Cstring)::Cstring
Cstring(@0x00007fff5fbffc45)

julia> unsafe_string(path)
"/bin/bash"

実際には、特に再利用可能な機能を提供する際に、一般的に @ccall の使用を引数を設定し、その後CまたはFortran関数が指定する方法でエラーをチェックするJulia関数にラップします。そして、エラーが発生した場合は、通常のJulia例外としてスローされます。これは特に重要です。なぜなら、CおよびFortranのAPIはエラー条件を示す方法が非常に一貫性がないからです。例えば、getenv Cライブラリ関数は、以下のJulia関数にラップされています。これは、env.jlからの実際の定義の簡略版です。

function getenv(var::AbstractString)
    val = @ccall getenv(var::Cstring)::Cstring
    if val == C_NULL
        error("getenv: undefined variable: ", var)
    end
    return unsafe_string(val)
end

Cのgetenv関数は、C_NULLを返すことでエラーを示しますが、他の標準C関数は、-1、0、1、その他の特別な値を返すなど、異なる方法でエラーを示します。このラッパーは、呼び出し元が存在しない環境変数を取得しようとした場合に、問題を示す例外をスローします:

julia> getenv("SHELL")
"/bin/bash"

julia> getenv("FOOBAR")
ERROR: getenv: undefined variable: FOOBAR

ここに、ローカルマシンのホスト名を発見する少し複雑な例があります。

function gethostname()
    hostname = Vector{UInt8}(undef, 256) # MAXHOSTNAMELEN
    err = @ccall gethostname(hostname::Ptr{UInt8}, sizeof(hostname)::Csize_t)::Int32
    Base.systemerror("gethostname", err != 0)
    hostname[end] = 0 # ensure null-termination
    return GC.@preserve hostname unsafe_string(pointer(hostname))
end

この例では、最初にバイトの配列を割り当てます。次に、Cライブラリ関数gethostnameを呼び出して、配列にホスト名を格納します。最後に、ホスト名バッファへのポインタを取得し、そのポインタをヌル終端のC文字列であると仮定してJuliaの文字列に変換します。

Cライブラリが呼び出し元にメモリを割り当てさせ、それを呼び出し先に渡して populated させるというパターンを使用するのは一般的です。このようにしてJuliaからメモリを割り当てることは、未初期化の配列を作成し、そのデータへのポインタをC関数に渡すことで一般的に達成されます。これが、ここで Cstring 型を使用しない理由です:配列が未初期化であるため、ヌルバイトを含む可能性があります。@ccall の一部として Cstring に変換すると、含まれているヌルバイトをチェックし、そのため変換エラーが発生する可能性があります。

unsafe_stringを使ってpointer(hostname)を逆参照することは、安全でない操作です。これは、hostnameに割り当てられたメモリにアクセスする必要があり、そのメモリがその間にガーベジコレクションされている可能性があるためです。マクロGC.@preserveは、これが発生するのを防ぎ、したがって無効なメモリ位置にアクセスすることを防ぎます。

最後に、パスを指定してライブラリを指定する例を示します。以下の内容で共有ライブラリを作成します。

#include <stdio.h>

void say_y(int y)
{
    printf("Hello from C: got y = %d.\n", y);
}

gcc -fPIC -shared -o mylib.so mylib.cを使用してコンパイルします。次に、ライブラリ名として(絶対)パスを指定することで呼び出すことができます:

julia> @ccall "./mylib.so".say_y(5::Cint)::Cvoid
Hello from C: got y = 5.

Creating C-Compatible Julia Function Pointers

Juliaの関数を、関数ポインタ引数を受け入れるネイティブC関数に渡すことは可能です。たとえば、次のようなCプロトタイプに一致させるために:

typedef returntype (*functiontype)(argumenttype, ...)

マクロ @cfunction は、Julia関数への呼び出しのためのC互換の関数ポインタを生成します。 4d61726b646f776e2e436f64652822222c2022406366756e6374696f6e2229_40726566 への引数は次のとおりです:

  1. ジュリアの関数
  2. 関数の戻り値の型
  3. 関数シグネチャに対応する入力タイプのタプル
Note

@ccallと同様に、戻り値の型と入力型はリテラル定数でなければなりません。

Note

現在、プラットフォームデフォルトのC呼び出し規約のみがサポートされています。これは、@cfunctionで生成されたポインタが、32ビットWindowsでWINAPIがstdcall関数を期待する呼び出しに使用できないことを意味しますが、WIN64では(stdcallがC呼び出し規約と統一されているため)使用できます。

Note

@cfunctionを介して公開されるコールバック関数はエラーをスローしてはいけません。そうすると、予期せずにJuliaランタイムに制御が戻り、プログラムが未定義の状態になる可能性があります。

標準Cライブラリのqsort関数の典型的な例は、次のように宣言されています:

void qsort(void *base, size_t nitems, size_t size,
           int (*compare)(const void*, const void*));

base 引数は、各要素が size バイトの長さ nitems の配列へのポインタです。 compare はコールバック関数で、2つの要素 ab へのポインタを受け取り、ab の前に来るべき場合はゼロ未満、後に来るべき場合はゼロより大きい整数を返します(または、任意の順序が許可される場合はゼロを返します)。

今、私たちがJuliaの値の1次元配列Aを持っていて、sort関数ではなくqsort関数を使用してソートしたいとします。qsortを呼び出して引数を渡すことを考える前に、比較関数を書く必要があります:

julia> function mycompare(a, b)::Cint
           return (a < b) ? -1 : ((a > b) ? +1 : 0)
       end;

qsort は C の int を返す比較関数を期待するため、戻り値の型を Cint として注釈を付けます。

この関数をCに渡すために、マクロ@cfunctionを使用してそのアドレスを取得します:

julia> mycompare_c = @cfunction(mycompare, Cint, (Ref{Cdouble}, Ref{Cdouble}));

@cfunction は3つの引数を必要とします:Julia関数(mycompare)、戻り値の型(Cint)、および入力引数の型のリテラルタプル。この場合、CdoubleFloat64)の要素をソートするためのものです。

最終的な qsort の呼び出しは次のようになります:

julia> A = [1.3, -2.7, 4.4, 3.1];

julia> @ccall qsort(A::Ptr{Cdouble}, length(A)::Csize_t, sizeof(eltype(A))::Csize_t, mycompare_c::Ptr{Cvoid})::Cvoid

julia> A
4-element Vector{Float64}:
 -2.7
  1.3
  3.1
  4.4

例に示すように、元のJulia配列 A は現在ソートされています: [-2.7, 1.3, 3.1, 4.4]。Julia takes care of converting the array to a Ptr{Cdouble})、要素タイプのサイズをバイト単位で計算することなど。

楽しむために、mycompareprintln("mycompare($a, $b)") 行を挿入してみてください。これにより、qsort が実行している比較を確認でき(実際に渡したJulia関数が呼び出されていることを確認できます)、デバッグに役立ちます。

Mapping C Types to Julia

宣言されたC型とJuliaでの宣言を正確に一致させることが重要です。不一致があると、あるシステムで正しく動作するコードが別のシステムで失敗したり、不確定な結果を生じたりする可能性があります。

C関数を呼び出すプロセスでは、どこにもCヘッダーファイルは使用されていないことに注意してください。あなたのJulia型と呼び出しシグネチャがCヘッダーファイルのものを正確に反映していることを確認する責任があります。[2]

Automatic Type Conversion

Juliaは、各引数を指定された型に変換するために、Base.cconvert関数への呼び出しを自動的に挿入します。たとえば、次の呼び出し:

@ccall "libfoo".foo(x::Int32, y::Float64)::Cvoid

そのように書かれたかのように振る舞います:

c_x = Base.cconvert(Int32, x)
c_y = Base.cconvert(Float64, y)
GC.@preserve c_x c_y begin
    @ccall "libfoo".foo(
        Base.unsafe_convert(Int32, c_x)::Int32,
        Base.unsafe_convert(Float64, c_y)::Float64
    )::Cvoid
end

Base.cconvert は通常 convert を呼び出しますが、Cに渡すのにより適した任意の新しいオブジェクトを返すように定義できます。これは、Cコードによってアクセスされるすべてのメモリの割り当てを行うために使用されるべきです。例えば、これはオブジェクトの Array(例えば、文字列)をポインタの配列に変換するために使用されます。

Base.unsafe_convertPtr タイプへの変換を処理します。これは、オブジェクトをネイティブポインタに変換することでガーベジコレクタからオブジェクトが隠され、早期に解放される可能性があるため、安全ではないと見なされます。

Type Correspondences

まず、関連するJuliaの型用語をいくつか確認しましょう:

Syntax / KeywordExampleDescription
mutable structBitSet"Leaf Type" :: A group of related data that includes a type-tag, is managed by the Julia GC, and is defined by object-identity. The type parameters of a leaf type must be fully defined (no TypeVars are allowed) in order for the instance to be constructed.
abstract typeAny, AbstractArray{T, N}, Complex{T}"Super Type" :: A super-type (not a leaf-type) that cannot be instantiated, but can be used to describe a group of types.
T{A}Vector{Int}"Type Parameter" :: A specialization of a type (typically used for dispatch or storage optimization).
"TypeVar" :: The T in the type parameter declaration is referred to as a TypeVar (short for type variable).
primitive typeInt, Float64"Primitive Type" :: A type with no fields, but a size. It is stored and defined by-value.
structPair{Int, Int}"Struct" :: A type with all fields defined to be constant. It is defined by-value, and may be stored with a type-tag.
ComplexF64 (isbits)"Is-Bits" :: A primitive type, or a struct type where all fields are other isbits types. It is defined by-value, and is stored without a type-tag.
struct ...; endnothing"Singleton" :: a Leaf Type or Struct with no fields.
(...) or tuple(...)(1, 2, 3)"Tuple" :: an immutable data-structure similar to an anonymous struct type, or a constant array. Represented as either an array or a struct.

Bits Types

特別なタイプがいくつかあり、他のタイプが同じように振る舞うように定義されることはありません:

  • Float32

    C(またはFortranのREAL*4)のfloat型に正確に対応しています。

  • Float64

    Cのdouble型(またはFortranのREAL*8)に正確に対応します。

  • 複素数F32

    C言語のcomplex float型(またはFortranのCOMPLEX*8)に正確に対応しています。

  • 複素数F64

    C言語のcomplex double型(またはFortranのCOMPLEX*16)に正確に対応します。

  • 署名済み

    Csigned型注釈(またはFortranの任意のINTEGER型)に正確に対応します。Signedのサブタイプでない任意のJulia型は、符号なしであると見なされます。

  • Ref{T}

    Ptr{T}のように振る舞い、JuliaのGCを介してメモリを管理できる。

  • Array{T,N}

    配列がCにPtr{T}引数として渡されるとき、それは再解釈キャストされません:Juliaは配列の要素型がTと一致することを要求し、最初の要素のアドレスが渡されます。

    したがって、Array にデータが誤った形式で含まれている場合は、trunc.(Int32, A) のような呼び出しを使用して明示的に変換する必要があります。

    配列 A を事前にデータを変換することなく異なる型のポインタとして渡すには(例えば、Float64 配列を解釈されていないバイトで動作する関数に渡すために)、引数を Ptr{Cvoid} として宣言することができます。

    もし Ptr{T} 型の配列が Ptr{Ptr{T}} 引数として渡されると、Base.cconvert は、最初に配列のヌル終端コピーを作成し、各要素をその 4d61726b646f776e2e436f64652822222c2022426173652e63636f6e766572742229_40726566 バージョンに置き換えようとします。これにより、例えば、Vector{String} 型の argv ポインタ配列を Ptr{Ptr{Cchar}} 型の引数に渡すことが可能になります。

現在サポートしているすべてのシステムにおいて、基本的なC/C++の値型は次のようにJulia型に変換される可能性があります。すべてのC型には、同じ名前の対応するJulia型があり、Cでプレフィックスが付けられています。これは、ポータブルなコードを書く際に役立ちます(CのintがJuliaのIntと同じではないことを思い出すため)。

システム独立型

C nameFortran nameStandard Julia AliasJulia Base Type
unsigned charCHARACTERCucharUInt8
bool (_Bool in C99+)CucharUInt8
shortINTEGER*2, LOGICAL*2CshortInt16
unsigned shortCushortUInt16
int, BOOL (C, typical)INTEGER*4, LOGICAL*4CintInt32
unsigned intCuintUInt32
long longINTEGER*8, LOGICAL*8ClonglongInt64
unsigned long longCulonglongUInt64
intmax_tCintmax_tInt64
uintmax_tCuintmax_tUInt64
floatREAL*4iCfloatFloat32
doubleREAL*8CdoubleFloat64
complex floatCOMPLEX*8ComplexF32Complex{Float32}
complex doubleCOMPLEX*16ComplexF64Complex{Float64}
ptrdiff_tCptrdiff_tInt
ssize_tCssize_tInt
size_tCsize_tUInt
voidCvoid
void and [[noreturn]] or _NoreturnUnion{}
void*Ptr{Cvoid} (or similarly Ref{Cvoid})
T* (where T represents an appropriately defined type)Ref{T} (T may be safely mutated only if T is an isbits type)
char* (or char[], e.g. a string)CHARACTER*NCstring if null-terminated, or Ptr{UInt8} if not
char** (or *char[])Ptr{Ptr{UInt8}}
jl_value_t* (any Julia Type)Any
jl_value_t* const* (a reference to a Julia value)Ref{Any} (const, since mutation would require a write barrier, which is not possible to insert correctly)
va_argNot supported
... (variadic function specification)T... (where T is one of the above types, when using the ccall function)
... (variadic function specification); va_arg1::T, va_arg2::S, etc. (only supported with @ccall macro)

Cstring 型は本質的に Ptr{UInt8} の同義語ですが、Juliaの文字列に埋め込まれたヌル文字が含まれている場合、Cstring への変換はエラーをスローします(これは、Cルーチンがヌルを終端子として扱う場合、文字列が静かに切り捨てられる原因となります)。ヌル終端を仮定しないCルーチンに char* を渡す場合(例えば、明示的な文字列の長さを渡す場合)や、Juliaの文字列にヌルが含まれていないことが確実であり、チェックをスキップしたい場合は、引数の型として Ptr{UInt8} を使用できます。Cstringccall の戻り値の型としても使用できますが、その場合は明らかに追加のチェックを導入せず、呼び出しの可読性を向上させることを目的としています。

システム依存型

C nameStandard Julia AliasJulia Base Type
charCcharInt8 (x86, x86_64), UInt8 (powerpc, arm)
longClongInt (UNIX), Int32 (Windows)
unsigned longCulongUInt (UNIX), UInt32 (Windows)
wchar_tCwchar_tInt32 (UNIX), UInt16 (Windows)
Note

Fortranを呼び出す際は、すべての入力をヒープまたはスタックに割り当てられた値へのポインタとして渡さなければならないため、上記のすべての型対応には、その型仕様の周りに追加の Ptr{..} または Ref{..} ラッパーを含める必要があります。

Warning

文字列引数(char*)の場合、Juliaの型はCstring(ヌル終端データが期待される場合)であるべきです。それ以外の場合は、Ptr{Cchar}またはPtr{UInt8}のいずれか(これらの2つのポインタ型は同じ効果があります)であるべきです。上記のように、Stringではありません。同様に、配列引数(T[]またはT*)の場合、Juliaの型は再びPtr{T}であるべきであり、Vector{T}ではありません。

Warning

JuliaのChar型は32ビットであり、これはすべてのプラットフォームでのワイドキャラクター型(wchar_tまたはwint_t)とは異なります。

Warning

Union{}の戻り値の型は、関数が戻らないことを意味します。つまり、C++11の[[noreturn]]やC11の_Noreturn(例:jl_throwlongjmp)です。戻り値がない(void)が実際には戻る関数にはこれを使用しないでください。その場合は、代わりにCvoidを使用してください。

Note

wchar_t* 引数の場合、Julia の型は Cwstring (C ルーチンがヌル終端文字列を期待する場合)またはそれ以外の場合は Ptr{Cwchar_t} であるべきです。また、Julia の UTF-8 文字列データは内部的にヌル終端されているため、ヌル終端データを期待する C 関数にコピーを作成せずに渡すことができます(ただし、Cwstring 型を使用すると、文字列自体にヌル文字が含まれている場合にエラーが発生します)。

Note

C関数は、char**型の引数を取る場合、Julia内でPtr{Ptr{UInt8}}型を使用して呼び出すことができます。例えば、次のようなC関数:

int main(int argc, char **argv);

以下のJuliaコードを介して呼び出すことができます:

argv = [ "a.out", "arg1", "arg2" ]
@ccall main(length(argv)::Int32, argv::Ptr{Ptr{UInt8}})::Int32
Note

Fortran関数が可変長文字列の型 character(len=*) を受け取る場合、文字列の長さは隠れた引数として提供されます。これらの引数の型と位置はコンパイラによって異なり、コンパイラベンダーは通常、Csize_t を型として使用し、隠れた引数を引数リストの最後に追加することをデフォルトとしています。この動作は一部のコンパイラ(GNU)では固定されていますが、他のコンパイラ(Intel、PGI)はオプションで隠れた引数を文字引数の直後に配置することを許可しています。例えば、次の形式のFortranサブルーチン

subroutine test(str1, str2)
character(len=*) :: str1,str2

次のJuliaコードを介して呼び出すことができ、長さが追加されます。

str1 = "foo"
str2 = "bar"
ccall(:test, Cvoid, (Ptr{UInt8}, Ptr{UInt8}, Csize_t, Csize_t),
                    str1, str2, sizeof(str1), sizeof(str2))
Warning

Fortran コンパイラは、ポインタ、仮定形 (:) および仮定サイズ (*) 配列のために他の隠れた引数を追加することがあります。このような動作は、ISO_C_BINDING を使用し、サブルーチンの定義に bind(c) を含めることで回避できます。これは相互運用可能なコードに対して強く推奨されます。この場合、隠れた引数は存在せず、いくつかの言語機能(例:文字列を渡すためには character(len=1) のみが許可されます)のコストがかかります。

Note

Cの関数がCvoidを返すように宣言されている場合、Juliaでは値nothingを返します。

Struct Type Correspondences

CやFortran90のTYPE(またはF77のいくつかのバリアントでのSTRUCTURE / RECORD)のような複合型は、同じフィールドレイアウトを持つstruct定義を作成することでJuliaでミラーリングできます。

再帰的に使用される場合、isbits 型はインラインで保存されます。他のすべての型はデータへのポインタとして保存されます。Cの中で値として使用される構造体をミラーリングする際には、フィールドを手動でコピーしようとしないことが重要です。そうしないと、正しいフィールドのアライメントが保持されません。代わりに、isbits 構造体型を宣言し、それを使用してください。無名構造体はJuliaへの翻訳では不可能です。

Juliaでは、パックされた構造体および共用体の宣言はサポートされていません。

unionの近似を得ることができます。事前に、最大のサイズを持つフィールド(パディングを含む可能性があります)を知っている場合です。フィールドをJuliaに翻訳する際には、Juliaのフィールドをその型のみに宣言してください。

パラメータの配列は NTuple を使って表現できます。例えば、C表記の構造体は次のように書かれます。

struct B {
    int A[3];
};

b_a_2 = B.A[2];

ジュリアでは次のように書くことができます。

struct B
    A::NTuple{3, Cint}
end

b_a_2 = B.A[3]  # note the difference in indexing (1-based in Julia, 0-based in C)

未知のサイズの配列([]または[0]で指定されたC99準拠の可変長構造体)は直接サポートされていません。これらに対処する最良の方法は、バイトオフセットを直接扱うことです。たとえば、Cライブラリが適切な文字列型を宣言し、それへのポインタを返した場合:

struct String {
    int strlen;
    char data[];
};

Juliaでは、文字列の部分に独立してアクセスして、その文字列のコピーを作成することができます:

str = from_c::Ptr{Cvoid}
len = unsafe_load(Ptr{Cint}(str))
unsafe_string(str + Core.sizeof(Cint), len)

Type Parameters

@ccall@cfunction への型引数は、使用が定義されているメソッドが定義されるときに静的に評価されます。したがって、リテラルタプルの形を取る必要があり、変数ではなく、ローカル変数を参照することはできません。

これは奇妙な制限のように聞こえるかもしれませんが、CはJuliaのような動的言語ではないため、その関数は静的に知られた固定のシグネチャを持つ引数の型のみを受け入れることを覚えておいてください。

しかし、C ABIを計算するためには型レイアウトが静的に知られている必要がありますが、関数の静的パラメータはこの静的環境の一部と見なされます。関数の静的パラメータは、型のレイアウトに影響を与えない限り、呼び出しシグネチャの型パラメータとして使用できます。例えば、f(x::T) where {T} = @ccall valid(x::Ptr{T})::Ptr{T}は有効です。なぜなら、Ptrは常にワードサイズのプリミティブ型だからです。しかし、g(x::T) where {T} = @ccall notvalid(x::T)::Tは無効です。なぜなら、Tの型レイアウトは静的に知られていないからです。

SIMD Values

注意: この機能は現在、64ビットx86およびAArch64プラットフォームでのみ実装されています。

もしC/C++のルーチンが引数または戻り値としてネイティブのSIMD型を持つ場合、対応するJulia型はSIMD型に自然にマッピングされるVecElementの同種タプルです。具体的には:

  • タプルはSIMDタイプと同じサイズでなければなりません。たとえば、x86上の__m128を表すタプルは16バイトのサイズを持つ必要があります。
  • タプルの要素タイプは、VecElement{T}のインスタンスでなければならず、Tは1、2、4、または8バイトのプリミティブ型である必要があります。

例えば、AVXインストリンシックを使用するこのCルーチンを考えてみてください:

#include <immintrin.h>

__m256 dist( __m256 a, __m256 b ) {
    return _mm256_sqrt_ps(_mm256_add_ps(_mm256_mul_ps(a, a),
                                        _mm256_mul_ps(b, b)));
}

以下のJuliaコードはccallを使用してdistを呼び出します:

const m256 = NTuple{8, VecElement{Float32}}

a = m256(ntuple(i -> VecElement(sin(Float32(i))), 8))
b = m256(ntuple(i -> VecElement(cos(Float32(i))), 8))

function call_dist(a::m256, b::m256)
    @ccall "libdist".dist(a::m256, b::m256)::m256
end

println(call_dist(a,b))

ホストマシンは必要なSIMDレジスタを持っている必要があります。たとえば、上記のコードはAVXサポートのないホストでは動作しません。

Memory Ownership

malloc/free

メモリの割り当てと解放は、使用しているライブラリの適切なクリーンアップルーチンへの呼び出しによって処理されなければなりません。これは、任意のCプログラムと同様です。JuliaでLibc.freeから受け取ったオブジェクトを解放しようとしないでください。これは、誤ったライブラリを介してfree関数が呼び出され、プロセスが中止される原因となる可能性があります。逆に、Juliaで割り当てられたオブジェクトを外部ライブラリによって解放させることも同様に無効です。

When to use T, Ptr{T} and Ref{T}

In Julia code wrapping calls to external C routines, ordinary (non-pointer) data should be declared to be of type T inside the @ccall, as they are passed by value. For C code accepting pointers, Ref{T} should generally be used for the types of input arguments, allowing the use of pointers to memory managed by either Julia or C through the implicit call to Base.cconvert. In contrast, pointers returned by the C function called should be declared to be of the output type Ptr{T}, reflecting that the memory pointed to is managed by C only. Pointers contained in C structs should be represented as fields of type Ptr{T} within the corresponding Julia struct types designed to mimic the internal structure of corresponding C structs.

Juliaコードで外部Fortranルーチンをラップする際、すべての入力引数はRef{T}型として宣言する必要があります。Fortranはすべての変数をメモリ位置へのポインタとして渡すためです。戻り値の型は、Fortranサブルーチンの場合はCvoid、または型Tを返すFortran関数の場合はTである必要があります。

Mapping C Functions to Julia

@ccall / @cfunction argument translation guide

Cの引数リストをJuliaに翻訳するには:

  • T は、次のいずれかの基本型です: charintlongshortfloatdoublecomplexenum またはそれらの typedef 等価物。

    • T、ここで T は上記の表に従った同等のJuliaビット型です。
    • Tenumである場合、引数の型はCintまたはCuintと同等である必要があります。
    • 引数の値はコピーされます(値渡し)
  • struct T(構造体へのtypedefを含む)

    • T、ここで T はJuliaのリーフタイプです
    • 引数の値はコピーされます(値渡し)
  • void*

    • このパラメータの使い方によります。まず、これを意図されたポインタ型に翻訳し、その後、このリストの残りのルールを使用してJuliaの同等のものを決定します。
    • この引数は、実際に未知のポインタである場合、Ptr{Cvoid}として宣言される可能性があります。
  • jl_value_t*

    • 任意
    • 引数の値は有効なJuliaオブジェクトでなければなりません。
  • jl_value_t* const*

    • Ref{任意}
    • 引数リストは有効なJuliaオブジェクト(またはC_NULL)でなければなりません。
    • 出力パラメータとして使用することはできません。ユーザーがオブジェクトをGCで保持するために別途手配できる場合を除きます。
  • T*

    • Ref{T}は、Tに対応するJuliaの型です。
    • 引数の値は、inlinealloc タイプの場合にコピーされます(isbits を含む)。それ以外の場合、値は有効な Julia オブジェクトでなければなりません。
  • T (*)(...)(例:関数へのポインタ)

    • Ptr{Cvoid}(このポインタを作成するには、明示的に @cfunction を使用する必要があるかもしれません)
  • ... (例: 可変引数)

    • [for ccall]: T...、ここで T は残りのすべての引数の単一のJulia型です
    • [for @ccall]: ; va_arg1::T, va_arg2::S, etc, ここで TS はJuliaの型です(すなわち、通常の引数と可変引数を ; で区切ります)
    • 現在 @cfunction によってサポートされていません
  • va_arg

    • ccall または @cfunction によってサポートされていません

@ccall / @cfunction return type translation guide

Cの戻り値の型をJuliaに翻訳するには:

    • Cvoid(これはシングルトンインスタンス nothing::Cvoid を返します)
  • T は、次のいずれかの基本型である: charintlongshortfloatdoublecomplexenum またはそれらの typedef 等価物

    • T、ここで T は上記の表に従った同等のJuliaビット型です。
    • Tenumである場合、引数の型はCintまたはCuintと同等である必要があります。
    • 引数の値はコピーされます(値渡しで返されます)
  • struct T(構造体へのtypedefを含む)

    • T、ここで T はJuliaのリーフタイプです
    • 引数の値はコピーされます(値渡しで返されます)
  • void*

    • このパラメータの使い方によります。まず、これを意図されたポインタ型に翻訳し、その後、このリストの残りのルールを使用してJuliaの同等のものを決定します。
    • この引数は、実際に未知のポインタである場合、Ptr{Cvoid}として宣言される可能性があります。
  • jl_value_t*

    • 任意
    • 引数の値は有効なJuliaオブジェクトでなければなりません。
  • jl_value_t**

    • Ptr{Any}Ref{Any}は戻り値の型として無効です)
  • T*

    • もしメモリがすでにJuliaによって所有されているか、isbits型であり、非nullであることが知られている場合:

      • Ref{T}は、Tに対応するJuliaの型です。
      • Ref{Any}の戻り値の型は無効です。Anyjl_value_t*に対応)またはPtr{Any}jl_value_t**に対応)である必要があります。
      • C は絶対に Tisbits 型である場合、Ref{T} を介して返されたメモリを変更してはいけません。
    • Cがメモリを所有している場合:

      • Ptr{T}は、TTに対応するJulia型であることを示します。
  • T (*)(...)(例:関数へのポインタ)

    • Ptr{Cvoid}を直接Juliaから呼び出すには、これを@ccallの最初の引数として渡す必要があります。Indirect Callsを参照してください。

Passing Pointers for Modifying Inputs

Cは複数の戻り値をサポートしていないため、C関数はしばしば関数が修正するデータへのポインタを受け取ります。これを@ccall内で実現するには、まず適切な型のRef{T}の中に値をカプセル化する必要があります。このRefオブジェクトを引数として渡すと、Juliaは自動的にカプセル化されたデータへのCポインタを渡します:

width = Ref{Cint}(0)
range = Ref{Cfloat}(0)
@ccall foo(width::Ref{Cint}, range::Ref{Cfloat})::Cvoid

戻ると、widthrange の内容は(foo によって変更された場合)width[]range[] で取得できます。つまり、ゼロ次元配列のように動作します。

C Wrapper Examples

シンプルな例として、Ptr型を返すCラッパーを始めましょう:

mutable struct gsl_permutation
end

# The corresponding C signature is
#     gsl_permutation * gsl_permutation_alloc (size_t n);
function permutation_alloc(n::Integer)
    output_ptr = @ccall "libgsl".gsl_permutation_alloc(n::Csize_t)::Ptr{gsl_permutation}
    if output_ptr == C_NULL # Could not allocate memory
        throw(OutOfMemoryError())
    end
    return output_ptr
end

GNU Scientific Library(ここでは:libgslを通じてアクセス可能であると仮定)では、C関数gsl_permutation_allocの戻り値の型として不透明ポインタgsl_permutation *が定義されています。ユーザーコードはgsl_permutation構造体の内部を見なくてよいため、対応するJuliaラッパーは内部フィールドを持たず、Ptr型の型パラメータに配置されることを唯一の目的とする新しい型宣言gsl_permutationが必要です。ccallの戻り値の型はPtr{gsl_permutation}として宣言されており、output_ptrによって指されるメモリはCによって制御されています。

入力 n は値渡しされるため、関数の入力シグネチャは単に ::Csize_t と宣言され、RefPtr は必要ありません。(ラッパーが Fortran 関数を呼び出している場合、対応する関数の入力シグネチャは ::Ref{Csize_t} になります。なぜなら、Fortran の変数はポインタで渡されるからです。)さらに、nCsize_t 整数に変換可能な任意の型であることができます;ccall は暗黙的に Base.cconvert(Csize_t, n) を呼び出します。

ここに対応するデストラクタをラップする第二の例があります:

# The corresponding C signature is
#     void gsl_permutation_free (gsl_permutation * p);
function permutation_free(p::Ptr{gsl_permutation})
    @ccall "libgsl".gsl_permutation_free(p::Ptr{gsl_permutation})::Cvoid
end

ここにジュリア配列を渡す第三の例があります:

# The corresponding C signature is
#    int gsl_sf_bessel_Jn_array (int nmin, int nmax, double x,
#                                double result_array[])
function sf_bessel_Jn_array(nmin::Integer, nmax::Integer, x::Real)
    if nmax < nmin
        throw(DomainError())
    end
    result_array = Vector{Cdouble}(undef, nmax - nmin + 1)
    errorcode = @ccall "libgsl".gsl_sf_bessel_Jn_array(
                    nmin::Cint, nmax::Cint, x::Cdouble, result_array::Ref{Cdouble})::Cint
    if errorcode != 0
        error("GSL error code $errorcode")
    end
    return result_array
end

C関数wrappedは整数エラーコードを返します。Bessel J関数の実際の評価の結果は、Julia配列result_arrayに格納されます。この変数はRef{Cdouble}として宣言されており、そのメモリはJuliaによって割り当てられ、管理されています。暗黙の呼び出しBase.cconvert(Ref{Cdouble}, result_array)は、JuliaポインタをCが理解できる形式のJulia配列データ構造に展開します。

Fortran Wrapper Example

以下の例では、ccallを使用して一般的なFortranライブラリ(libBLAS)の関数を呼び出し、ドット積を計算します。引数のマッピングは上記とは少し異なることに注意してください。JuliaからFortranへのマッピングが必要です。すべての引数タイプに対して、RefまたはPtrを指定します。このマングリング規則は、使用しているFortranコンパイラやオペレーティングシステムに特有であり、文書化されていない可能性があります。ただし、各引数をRef(または同等のPtr)でラップすることは、Fortranコンパイラの実装において一般的な要件です。

function compute_dot(DX::Vector{Float64}, DY::Vector{Float64})
    @assert length(DX) == length(DY)
    n = length(DX)
    incx = incy = 1
    product = @ccall "libLAPACK".ddot(
        n::Ref{Int32}, DX::Ptr{Float64}, incx::Ref{Int32}, DY::Ptr{Float64}, incy::Ref{Int32})::Float64
    return product
end

Garbage Collection Safety

データを @ccall に渡す際は、pointer 関数の使用を避けるのが最善です。代わりに、Base.cconvert メソッドを定義し、変数を直接 @ccall に渡してください。@ccall は、自動的にすべての引数が呼び出しが返るまでガーベジコレクションから保護されるように手配します。C API が Julia によって割り当てられたメモリへの参照を保存する場合、@ccall が返った後にオブジェクトがガーベジコレクタに見える状態を保つ必要があります。これを行うための推奨方法は、C ライブラリがそれらを使い終わったことを通知するまで、これらの値を保持するために Array{Ref,1} 型のグローバル変数を作成することです。

ポインタをJuliaデータに作成した場合、ポインタの使用が終了するまで元のデータが存在することを確認する必要があります。unsafe_loadStringなど、Juliaの多くのメソッドはバッファの所有権を取得するのではなくデータのコピーを作成するため、元のデータを自由に解放(または変更)してもJuliaに影響を与えないことが安全です。注目すべき例外はunsafe_wrapで、パフォーマンスの理由から、基盤となるバッファを共有(または所有権を取得するように指示することができる)します。

ガーベジコレクタはファイナライズの順序を保証しません。つまり、ab への参照を含んでいて、ab の両方がガーベジコレクションの対象である場合、ba の後にファイナライズされる保証はありません。a の適切なファイナライズが b の有効性に依存する場合は、他の方法で対処する必要があります。

Non-constant Function Specifications

場合によっては、必要なライブラリの正確な名前やパスが事前にわからず、実行時に計算する必要があります。そのような場合に対処するために、ライブラリコンポーネントの仕様は関数呼び出しであることができます。例えば、find_blas().dgemmのようにです。この呼び出し式は、ccall自体が実行されるときに実行されます。ただし、一度決定されたライブラリの場所は変更されないと仮定されているため、呼び出しの結果はキャッシュされ再利用されることができます。したがって、式が実行される回数は不明であり、複数回の呼び出しで異なる値を返すことは未定義の動作を引き起こします。

もしさらに柔軟性が必要な場合、次のように eval を通じて計算された値を関数名として使用することが可能です:

@eval @ccall "lib".$(string("a", "b"))()::Cint

この式は string を使用して名前を構築し、その後この名前を新しい @ccall 式に置き換え、それが評価されます。eval はトップレベルでのみ動作するため、この式内ではローカル変数は利用できません(その値が $ で置き換えられない限り)。このため、eval は通常、ライブラリをラップする際のように、多くの類似関数を含むトップレベルの定義を形成するためにのみ使用されます。@cfunction に対しても同様の例を構築できます。

しかし、これを行うと非常に遅くなり、メモリが漏れる可能性があるため、通常はこれを避け、代わりに読み続けるべきです。次のセクションでは、間接呼び出しを使用して効率的に同様の効果を達成する方法について説明します。

Indirect Calls

@ccall の最初の引数は、実行時に評価される式でも構いません。この場合、式は Ptr に評価される必要があり、呼び出すネイティブ関数のアドレスとして使用されます。この動作は、最初の @ccall 引数がローカル変数、関数引数、または非定数のグローバル変数などの非定数への参照を含む場合に発生します。

例えば、dlsymを介して関数を検索し、そのセッションのために共有参照にキャッシュすることができます。例えば:

macro dlsym(lib, func)
    z = Ref{Ptr{Cvoid}}(C_NULL)
    quote
        let zlocal = $z[]
            if zlocal == C_NULL
                zlocal = dlsym($(esc(lib))::Ptr{Cvoid}, $(esc(func)))::Ptr{Cvoid}
                $z[] = zlocal
            end
            zlocal
        end
    end
end

mylibvar = Libdl.dlopen("mylib")
@ccall $(@dlsym(mylibvar, "myfunc"))()::Cvoid

Closure cfunctions

最初の引数 @cfunction$ でマークすることができ、その場合、戻り値は引数を閉じ込める struct CFunction になります。この戻りオブジェクトがすべての使用が完了するまで生き続けることを確認する必要があります。cfunction ポインタの内容とコードは、この参照がドロップされ、atexit で finalizer によって消去されます。これは通常必要ありませんが、この機能は C には存在しないため、別のクロージャ環境パラメータを提供しない設計の悪い API を扱う際に便利です。

function qsort(a::Vector{T}, cmp) where T
    isbits(T) || throw(ArgumentError("this method can only qsort isbits arrays"))
    callback = @cfunction $cmp Cint (Ref{T}, Ref{T})
    # Here, `callback` isa Base.CFunction, which will be converted to Ptr{Cvoid}
    # (and protected against finalization) by the ccall
    @ccall qsort(a::Ptr{T}, length(a)::Csize_t, Base.elsize(a)::Csize_t, callback::Ptr{Cvoid})
    # We could instead use:
    #    GC.@preserve callback begin
    #        use(Base.unsafe_convert(Ptr{Cvoid}, callback))
    #    end
    # if we needed to use it outside of a `ccall`
    return a
end
Note

Closure @cfunction は、すべてのプラットフォーム(例えば ARM や PowerPC)で利用できない LLVM トランポリンに依存しています。

Closing a Library

ライブラリを閉じて(アンロード)再ロードできるようにすることは、時には便利です。たとえば、Juliaで使用するCコードを開発しているとき、Cコードをコンパイルし、JuliaからCコードを呼び出し、その後ライブラリを閉じて、編集を行い、再コンパイルし、新しい変更をロードする必要があるかもしれません。Juliaを再起動するか、Libdl関数を使用してライブラリを明示的に管理することができます。例えば:

lib = Libdl.dlopen("./my_lib.so") # Open the library explicitly.
sym = Libdl.dlsym(lib, :my_fcn)   # Get a symbol for the function to call.
@ccall $sym(...) # Use the pointer `sym` instead of the library.symbol tuple.
Libdl.dlclose(lib) # Close the library explicitly.

@ccallを使用する際は、入力(例:@ccall "./my_lib.so".my_fcn(...)::Cvoid)に注意してください。ライブラリは暗黙的にオープンされ、明示的にクローズされない場合があります。

Variadic function calls

可変引数のC関数を呼び出すには、引数リスト内で セミコロン を使用して必須引数と可変引数を区切ることができます。以下に printf 関数の例を示します:

julia> @ccall printf("%s = %d\n"::Cstring ; "foo"::Cstring, foo::Cint)::Cint
foo = 3
8

ccall interface

@ccallには別の代替インターフェースがあります。このインターフェースは少し便利さに欠けますが、calling conventionを指定することができます。

ccallへの引数は次のとおりです:

  1. A (:function, "library") ペア(最も一般的)

    または

    :function 名のシンボルまたは "function" 名の文字列(現在のプロセスまたは libc のシンボル用)、

    または

    関数ポインタ(例えば、dlsymから)。

  2. 関数の戻り値の型

  3. 関数シグネチャに対応する入力タイプのタプル。一般的な間違いの一つは、引数タイプの1タプルは末尾にカンマを付けて書かなければならないことを忘れることです。

  4. 関数に渡される実際の引数の値(ある場合);それぞれが別々のパラメータです。

Note

(:function, "library")ペア、戻り値の型、および入力型はリテラル定数でなければなりません(つまり、変数ではなく、ただしNon-constant Function Specificationsを参照してください)。

残りのパラメータは、含まれているメソッドが定義されるときにコンパイル時に評価されます。

マクロと関数インターフェース間の翻訳の表は以下に示されています。

@ccallccall
@ccall clock()::Int32ccall(:clock, Int32, ())
@ccall f(a::Cint)::Cintccall(:a, Cint, (Cint,), a)
@ccall "mylib".f(a::Cint, b::Cdouble)::Cvoidccall((:f, "mylib"), Cvoid, (Cint, Cdouble), (a, b))
@ccall $fptr.f()::Cvoidccall(fptr, f, Cvoid, ())
@ccall printf("%s = %d\n"::Cstring ; "foo"::Cstring, foo::Cint)::Cint<unavailable>
@ccall printf("%s = %s\n"::Cstring ; "2 + 2"::Cstring, "5"::Cstring)::Cintccall(:printf, Cint, (Cstring, Cstring...), "%s = %s\n", "2 + 2", "5")
<unavailable>ccall(:gethostname, stdcall, Int32, (Ptr{UInt8}, UInt32), hn, length(hn))

Calling Convention

ccallの第二引数(戻り値の型の直前)は、オプションで呼び出し規約の指定子を指定できます(現在、@ccallマクロは呼び出し規約を指定することをサポートしていません)。指定子がない場合、プラットフォームのデフォルトのC呼び出し規約が使用されます。他にサポートされている規約は、stdcallcdeclfastcall、およびthiscall(64ビットWindowsではノーオプ)です。例えば(base/libc.jlから)、上記と同じgethostnameccallが見られますが、Windows用の正しいシグネチャが付いています:

hn = Vector{UInt8}(undef, 256)
err = ccall(:gethostname, stdcall, Int32, (Ptr{UInt8}, UInt32), hn, length(hn))

詳細については、LLVM Language Referenceをご覧ください。

特別な呼び出し規約 llvmcall があり、これによりLLVMのインストリンシックを直接呼び出すことができます。これは、GPGPUのような珍しいプラットフォームをターゲットにする際に特に便利です。例えば、CUDA の場合、スレッドインデックスを読み取る必要があります。

ccall("llvm.nvvm.read.ptx.sreg.tid.x", llvmcall, Int32, ())

ccallと同様に、引数のシグネチャを正確に取得することが重要です。また、Core.Intrinsicsによって公開されている同等のJulia関数とは異なり、内在的なものが現在のターゲットで意味を持ち、機能することを保証する互換性レイヤーは存在しないことに注意してください。

Accessing Global Variables

ネイティブライブラリによってエクスポートされたグローバル変数は、cglobal 関数を使用して名前でアクセスできます。4d61726b646f776e2e436f64652822222c202263676c6f62616c2229_40726566 への引数は、ccall で使用されるのと同じシンボル仕様と、変数に格納されている値を説明する型です:

julia> cglobal((:errno, :libc), Int32)
Ptr{Int32} @0x00007f418d0816b8

結果は、値のアドレスを示すポインタです。このポインタを使用して、unsafe_loadunsafe_store! を介して値を操作できます。

Note

この errno シンボルは、「libc」というライブラリには見つからないかもしれません。これは、あなたのシステムコンパイラの実装の詳細です。通常、標準ライブラリのシンボルは名前だけでアクセスされるべきであり、コンパイラが正しいものを補完します。しかし、この例に示されている errno シンボルはほとんどのコンパイラで特別なものであり、ここで見られる値はおそらくあなたが期待するものや望むものではありません。マルチスレッド対応のシステムでCの同等のコードをコンパイルすると、通常は異なる関数(マクロプリプロセッサのオーバーロードを介して)を実際に呼び出し、ここに表示されている従来の値とは異なる結果を返す可能性があります。

Accessing Data through a Pointer

以下のメソッドは「安全でない」として説明されています。なぜなら、悪いポインタや型宣言が原因でJuliaが突然終了する可能性があるからです。

与えられた Ptr{T} に対して、型 T の内容は一般的に unsafe_load(ptr, [index]) を使用して参照されたメモリからJuliaオブジェクトにコピーできます。インデックス引数はオプションであり(デフォルトは1)、Juliaの1ベースのインデックス付けの規則に従います。この関数は、getindex および setindex! の動作に意図的に似ています(例:[] アクセス構文)。

戻り値は、参照されたメモリの内容のコピーを含むように初期化された新しいオブジェクトになります。参照されたメモリは安全に解放またはリリースできます。

もし TAny であれば、メモリには Julia オブジェクトへの参照(jl_value_t*)が含まれていると仮定され、結果はこのオブジェクトへの参照となり、オブジェクトはコピーされません。この場合、オブジェクトが常にガーベジコレクタに見えるように注意する必要があります(ポインタはカウントされませんが、新しい参照はカウントされます)ので、メモリが早期に解放されないようにしてください。オブジェクトが元々 Julia によって割り当てられていなかった場合、新しいオブジェクトは決して Julia のガーベジコレクタによって最終化されることはありません。もし Ptr 自体が実際に jl_value_t* であれば、unsafe_pointer_to_objref(ptr) を使って再び Julia オブジェクト参照に変換することができます。(Julia 値 v は、pointer_from_objref(v) を呼び出すことで Ptr{Cvoid} ポインタに変換できます。)

逆操作(Ptr{T} にデータを書き込むこと)は、unsafe_store!(ptr, value, [index]) を使用して実行できます。現在、これはプリミティブ型または他のポインタフリー(isbits)の不変構造体型にのみサポートされています。

エラーをスローする操作は、おそらく現在未実装であり、解決できるようにバグとして投稿されるべきです。

もし関心のあるポインタがプレーンデータ配列(プリミティブ型または不変構造体)の場合、関数 unsafe_wrap(Array, ptr,dims, own = false) がより便利かもしれません。最終パラメータは、Juliaが基盤となるバッファの「所有権を取得」し、返された Array オブジェクトが最終化されるときに free(ptr) を呼び出すべきであれば true であるべきです。own パラメータが省略されるか false の場合、呼び出し元はすべてのアクセスが完了するまでバッファが存在し続けることを保証しなければなりません。

Ptr型における算術演算(例えば+を使用すること)は、Cのポインタ算術とは異なります。Juliaでは、Ptrに整数を加えると、常にポインタがバイト単位で移動します。これにより、ポインタ算術から得られるアドレス値は、ポインタの要素型に依存しません。

Thread-safety

いくつかのCライブラリは、異なるスレッドからコールバックを実行します。Juliaはスレッドセーフではないため、いくつかの追加の注意が必要です。特に、2層のシステムを設定する必要があります:Cコールバックは、あなたの「実際の」コールバックの実行をスケジュールするだけでなければなりません(Juliaのイベントループを介して)。これを行うには、AsyncConditionオブジェクトを作成し、それに対してwaitを呼び出します。

cond = Base.AsyncCondition()
wait(cond)

Cに渡すコールバックは、ccall:uv_async_sendに実行するだけで、cond.handleを引数として渡し、Juliaランタイムとの割り当てやその他の相互作用を避けるようにしてください。

イベントは統合される可能性があるため、uv_async_sendへの複数の呼び出しが条件への単一のウェイクアップ通知につながる場合があります。

More About Callbacks

Cライブラリにコールバックを渡す方法の詳細については、次のリンクを参照してください: blog post

C++

C++ バインディングを作成するためのツールについては、CxxWrap パッケージを参照してください。

  • 1Non-library function calls in both C and Julia can be inlined and thus may have even less overhead than calls to shared library functions. The point above is that the cost of actually doing foreign function call is about the same as doing a call in either native language.
  • 2The Clang package can be used to auto-generate Julia code from a C header file.