我一定错过了什么。我一直在读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会处理这个吗?我在这里缺少什么?
提前致谢。
答案 0 :(得分:5)
Ruby FFI尝试对谁拥有内存进行对称 - 如果你分配内存(即C代码),你必须释放它。相反,如果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);
答案 1 :(得分:4)
我认为在许多ffi中,无论使用哪种语言,必须使用专门提供的运行时函数构建内置类型(如字符串)的值。 Ruby遵守这条规则:
请参阅此article以获取有关该问题的快速教程,来自语言作者,对于该语言的1.8版本。
如果您坚持在代码中分配该数据块(使用C ++或纯C) - 在使用该扩展之后 - 最安全的路径可能是用结构包装它,并使用所谓的托管由ffi提供的struct工具将一个dispose函数连接到你的数据(你也必须编写),这样ruby知道如何在不再需要数据时释放数据。 但是您也可以简单地将您的数据声明为ruby中的指针(看起来这就是您所做的),并要求userland明确地释放该数据(再次使用您的扩展提供的dispose函数)。
这是another page演示托管结构的使用。
最后,不要忘记将要导出到ruby的任何C ++函数限定为extern "C"
(如果您还没有这样做)。