LoginSignup
0
1

More than 5 years have passed since last update.

x10からC++の関数を呼ぶ

Last updated at Posted at 2015-02-03

はじめに

x10はある程度は最適化してくれるものの、やはり性能はチューニングしたC,C++にはおよびません。そこでプログラムのホットスポットの関数のみC,C++で書いて、その関数をx10のコードから呼ぶことができます。
またC,C++の方がコミュニティが大きい分、便利なライブラリが多く、そのライブラリをそのまま利用できると嬉しいことがあります。

ここではnative版(C++版)x10からC++の関数を呼ぶ方法について紹介します。

参考:

C++の関数を呼ぶ

コードサンプル

MyCpp.hpp, MyCpp.cpp で宣言・定義された関数を呼ぶサンプル

NativeCppTest.x10
import x10.io.Console;
import x10.compiler.Native;
import x10.compiler.NativeCPPInclude;
import x10.compiler.NativeCPPCompilationUnit;

@NativeCPPInclude("MyCpp.hpp")
@NativeCPPCompilationUnit("MyCpp.cpp")

public class NativeCppTest {
  @Native("c++", "my_double(#1);")
  native static def double(n:Int): Int;

  public static def main( args: Rail[String] ) {
    @Native("c++", "foo();") {}

    val n = 30n;
    val n2 = double(n);
    Console.OUT.println("n2: " + n2);
  }
}
MyCpp.hpp
void foo();
int my_double(int n);
MyCpp.cpp
#include <iostream>
#include "MyCpp.hpp"

void foo() {
  std::cout << "hello from foo" << std::endl;
}

int my_double(int n) {
  return 2 * n;
}

関数を呼ぶときは2通りの書き方があります。

Native Static Method

C++関数をx10のstaticメソッドにマッピングする。

@Native("c++", "my_double(#1)")
native static def double(n:Int): Int;
  • メソッドの引数は #1, #2 に埋め込まれてC++の関数が呼ばれます。
  • native修飾子をつけると、x10での関数定義(native関数が見つからない場合に呼ばれる)ができなくなります。
    • つまりこの例ではmanaged版(Java版)のコンパイルはできません。
  • 少なくともprimitive型は返り値にできます。ただし型推論はできないので、返り値の型(この場合はInt)とパラメータの型を明示する必要があります。
  • 外部のC++ファイル@NativeCPPInclude でヘッダファイル、 @NativeCPPCompilationUnit でcppファイルをそれぞれ指定します。

Native Blocks

@Native("c++", "foo();") {}
  • このように書いてx10のメソッドを作らずに呼ぶこともできます。{}は必須です。x10のstatementを@Nativeの中身で置き換えています。
  • もし上記のコードがjavaにcompileされた場合、空のstatementが実行されるはずです。(つまり何も実行されない)
  • 引数を渡す方法はない(?)が、static変数を埋め込むことができます。

コンパイル方法

cpp,hpp,x10のファイルをすべて同一のディレクトリに置いている場合は、単純に以下のコマンドでコンパイルできます。

$ x10c++ NativeCppTest.x10

外部のライブラリをリンクする場合、インクルードパスとライブラリパス、リンクオプションを次のように指定します。

x10c++ Test.x10 -post '# # -I /usr/local/blas # -L /usr/local/blas -lblas'

最初の # はコンパイラの実行コマンド(g++とか)、二つ目はC++のファイルのリストとCXXFLAGS、3つ目はリンカオプションが埋め込まれ、埋め込まれた文字列が実行されます。

Stringを引数に渡す方法

primitive型の変数はC++の適切な型の変数にマッピングされます。
しかし文字列の場合は一工夫必要です。

例えば、文字列を引数として渡してsystemコマンドを実行するメソッドを考えましょう。
次のように書くことができます。

@Native("c++", "system( (#1)->c_str() );")
native static def system(cmd:String):Int;

x10のStringをC++に変換すると x10::lang::String というC++のクラスに変換され、そのクラスの中に c_str() というメソッドが定義されています。C++の std::string#c_str() と同じくchar*を返します。

配列を引数に渡す方法

Railの0番目の要素のアドレスをCに渡す。

NativeRail.x10
import x10.compiler.*;

@NativeCPPInclude("foo.h")
@NativeCPPCompilationUnit("foo.c")
public class NativeTest {

    @Native("c++", "foo((double*)(&((#1)->raw[0])), (int*)(&((#2)->raw[0])))")
    native static def foo(tmp:Rail[double],tmp2:Rail[int]):Int;

    public static def main(args:Rail[String]) {
        t:Rail[double] = new Rail[double](3);
        t2:Rail[int] = new Rail[int](3);
    foo(t,t2);
    for(d in t) Console.OUT.println(d);
    for(i in t2) Console.OUT.println(i);
    }
}
foo.c
int foo(double * tmp, int * tmp2) {
  tmp[0]=10.0;
  tmp[1]=11.0;
  tmp2[2]=21;
}

わかっていないこと

  • メモリ解放はどのタイミングで?
    • GCは効かない?たぶんC++側で明示的に解放する必要があると思うが、よくわかっていません。
  • C++のクラスをX10から参照することができる?
    • X10のクラスは上のStringの例のようにC++のクラスにマップされます。
      • ユーザー定義型の場合はビルド時に生成されるC++のコードを見れば、どのようにC++のクラスにマップされたかわかります。
    • しかし、C++のオブジェクトを指す変数をx10側で参照することができるかは不明です。
0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1