Распределение памяти FFI / MemoryPointer

Я должен что-то упустить. Я читал о 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поднять чар *, но никогда не освобождая его. FFI справляется с этим? Что мне здесь не хватает?

Заранее спасибо.

4

Решение

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);
5

Другие решения

Я думаю, что во многих языках, независимо от того, какой язык вы используете, значения встроенных типов (например, строк) должны создаваться с использованием специально предусмотренных функций времени выполнения. Ruby соблюдает это правило:

Видеть это статья краткое руководство по этому вопросу от автора языка для версии 1.8 языка.

Если вы настаиваете на выделении этого куска данных в своем коде (используя C ++ или обычный C) — в конце концов, в этом и заключается смысл использования этого расширения — возможно, самый безопасный путь — обернуть его структурой и использовать так называемое средство управляемой структуры при условии с помощью ffi подключить функцию dispose к вашим данным (которую вы тоже должны будете написать), чтобы ruby ​​знал, как освободить данные, когда они больше не нужны.
Но вы также можете просто объявить ваши данные в качестве указателя в ruby ​​(кажется, что это то, что вы сделали) и попросить пользовательскую область явно освободить эти данные (снова используя функцию dispose, предоставляемую вашим расширением).

Вот другая страница демонстрируя использование управляемых структур.

Наконец, не забудьте указать любую функцию C ++, которую вы хотите экспортировать в ruby, как extern "C" (если вы этого еще не сделали).

4

По вопросам рекламы [email protected]