4

私は何かが欠けているに違いありません。私はFFIについて読んでいますが、これについて明確な答えを得ることができないようです。次のC++関数があるとしましょう。

extern "C" {
  int ReturnAnArrayOfStrings(const char* arrayOfStrings[]) {
    if( NULL == arrayOfStrings ) return someCharList.size();

    for(auto iter = someCharList.begin(), auto index = 0; iter != someCharList.end(); ++iter, ++index) {
        char* allocatedHere = new char[strlen(*iter)]; // note that this is not freed
        strcpy_s(allocatedHere, strlen(*iter), *iter);
        arrayOfStrings[index] = allocatedHere;
    }

    return someCharList.size();
  }
}

私の知る限り、FFIからこれを使用する場合、あなたがしなければならないのは次のことだけです。

module SomeDll
  extend FFI::Library
  ffi_lib 'SomeDll.dll'
  attach_function :get_strings, :ReturnAnArrayOfStrings, [:pointer], :int
end

include SomeDll
pointer = FFI::MemoryPointer.new :pointer, get_strings(nil)  # how many strings are there?
get_strings pointer
pointer.get_array_of_string(0).each do |value|
  puts value
end

私の質問はこれです:誰がメモリをクリーンアップしますか?C ++メソッドはnew、char *を起動していますが、解放することはありません。FFIはこれを処理しますか?ここで何が欠けていますか?

前もって感謝します。

4

2 に答える 2

5

Ruby FFI は、誰がメモリを所有するかについて対称的であるように努めます。メモリを割り当てた場合 (つまり、C コード)、解放する必要があります。逆に、FFI が割り当てた場合は、FFI だけで解放できます。

FreeStrings() 関数を投稿しませんでしたが、次のように見えると仮定します。

void FreeStringArray(char **strings, int len) {
    for (int i = 0; i < len; ++i) {
        delete[] strings[i];
    }
    // Do _NOT_ free 'strings' itself, that is managed by FFI
}

そして、あなたはそれを次のように使用します:

module SomeDll
  extend FFI::Library
  ffi_lib 'SomeDll.dll'
  attach_function :get_strings, :ReturnAnArrayOfStrings, [:pointer], :int
  attach_function :free_strings, :FreeStringArray, [ :pointer, :int ], :void
end

include SomeDll

count = get_strings(nil)
strings = FFI::MemoryPointer.new :pointer, count
get_strings strings
strings.get_array_of_string(0, count).each do |value|
  puts value
end

# free each element of the array
free_strings(strings, count)

それならうまくいくはずです。

同等の C コードは次のようになります。

int count = ReturnArrayOfStrings(NULL);

// Allocate an array for the pointers.  i.e. FFI::MemoryPointer.new :pointer, count
char **ptr_array = (char **) calloc(count, sizeof(char *));

ReturnArrayOfStrings(ptr_array);
for (int i = 0; i < count; ++i) {
    printf("string[%d]=%s\n", i, ptr_array[i]);
}

// Free each element of the array (but not the array itself)
FreeStringArray(ptr_array, count);

// free the array itself. i.e FFI::MemoryPointer garbage-collecting its  memory
free(ptr_array);
于 2012-12-28T21:41:28.690 に答える
4

多くのffiでは、どの言語を使用する場合でも、組み込み型(文字列など)の値は、特別に提供されたランタイム関数を使用して構築する必要があると思います。Rubyはこのルールに従います。

言語のバージョン1.8については、言語の作成者によるこの問題の簡単なチュートリアルについては、この記事を参照してください。

コード内でそのデータチャンクを割り当てることを主張する場合(C ++またはプレーンCを使用)-結局のところ、その拡張機能を使用するポイントです-おそらく最も安全なパスは、それを構造体でラップし、提供されているいわゆる管理構造体機能を使用することですffiを使用して、dispose関数をデータに接続します(これも書き込む必要があります)。これにより、rubyは、データが不要になったときにデータを解放する方法を認識します。ただし、データをruby内のポインターとして宣言し(これはあなたが行ったことのようです)、ユーザーランドにそのデータを明示的に解放するように依頼することもできます(ここでも拡張機能によって提供されるdispose関数を使用します)。

これは、管理構造体の使用法を示す別のページです。

最後に、rubyにエクスポートするC ++関数をextern "C"(まだ行っていない場合は)修飾することを忘れないでください。

于 2012-12-28T09:37:03.407 に答える