2012-12-28 2 views
4

뭔가를 놓치고 있어야합니다. 나는 FFI에 대해 읽었으며 이에 대한 분명한 대답을 얻지 못하는 것 같습니다.FFI/MemoryPointer 메모리 할당

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 

내 질문은 이것이다 : 다음 FFI에서이를 사용하는 경우

내가 무엇을 말할 수에서
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(); 
    } 
} 

, 모두 당신이해야 할 것입니다 : 이제 나는 다음과 같은 C++ 기능이 있다고 가정 해 봅시다 : 누가 그 기억을 깨끗하게합니까? C++ 방법은 숯불을 꾸미고 결코 풀어주지 않고 new '입니다. FFI가 이것을 처리합니까? 내가 여기서 무엇을 놓치고 있니? 사전에

감사합니다.

답변

5

Ruby FFI는 누가 메모리를 소유했는지에 대해 대칭을 둡니다. 할당하면 (즉, C 코드) 자유롭게해야합니다. 반대로 FFI가 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 
} 

을 그리고 당신은 thusly 히 사용 :

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); 
+0

네, 그게 정확히 제가 한 일입니다. 명확한 코드 설명 주셔서 감사합니다! – Levi

4

나는 많은 ffi에서 당신이 사용하는 언어가 무엇이든, 내장 된 유형 (문자열과 같은)의 값은 특별히 제공되는 런타임 기능을 사용하여 구성되어야한다고 생각한다. Ruby는 다음 규칙을 준수합니다.

언어 작성자의 언어 버전 1.8에 대한 빠른 자습서는 article을 참조하십시오.

(C++ 또는 일반 C를 사용하여) 코드에서 해당 데이터 청크를 할당하려고한다면 - 그 확장 코드를 사용하는 시점 이후 - 가장 안전한 경로는 아마도 struct로 랩핑하고 소위 말하는 ffi가 제공하는 관리되는 구조체 기능을 사용하여 데이터에 처리 기능을 연결합니다.이 함수는 더 이상 필요하지 않으면 데이터를 확보하는 방법을 루비가 알 수 있도록 작성해야합니다. 그러나 단순히 루비 내에서 데이터를 포인터로 선언 할 수도 있습니다 (이것이 사용자가 한 것처럼 보입니다). 그리고 userland에게 명시 적으로 해당 데이터를 해제하도록 요청합니다 (다시 extention에서 제공하는 dispose 함수를 사용).

여기에 another page은 관리 구조체의 사용을 보여줍니다.

마지막으로, 루비로 내보낼 C++ 함수를 extern "C" (이미 작성하지 않은 경우)으로 한정한다는 것을 잊지 마십시오.

+0

감사합니다. 나는 간결함을 위해'extern "C"'를 포함하지 않았지만, 나는 명확성을 위해 질문을 편집 할 것이다. 다시 한 번 감사드립니다! – Levi

+0

뭔가 빠져있을 수도 있지만 다음의 C 메소드'void FreeStrings (const char * strings [], const int numberOfStrings)'를 제공했습니다. 루비에서 메모리를 할당 한 후'FreeStrings'을 호출하지 않고'FreeStrings'을 호출하는 것 사이에 차이점을 발견하지 못했습니다 (Ruby.exe의 Process Explorer에서 작업 세트를 관찰 할 때). 아니면 내가 당신이 말한 것을 오해했을 수도 있습니다. 그리고'FFI :: MemoryPointer'를 사용하고 있기 때문에 그것에 대해 걱정할 필요가 없습니까? – Levi

+0

FFI는 할당 된 포인터 (요청 된 포인터 수만큼의 공간)를 처리하지만 get_strings이 할당 한 메모리는 아무것도 모릅니다.이를 정리해야합니다. –