2011-01-25 6 views
8

C 라이브러리 용 하스켈 래퍼를 만들려고합니다. 기본 구조체는 명시 적 형식으로 표현하기에는 너무 복잡하며 C 함수 간 전달 이외의 다른 용도로 사용하지 않으므로 GHC에서 나를 대신 사용하도록 EmptyDataDecls을 사용하고 있습니다.저장 가능한 빈 데이터 선언

이 데이터 유형 중 하나에 대한 포인터가 필요하지만 alloca으로 데이터를 만들려고하면 데이터 유형이 Storable이 아니란 점에 대해 불만을 토로합니다. 예 :

{-# LANGUAGE ForeignFunctionInterface, EmptyDataDecls #-} 

module Main where 

import Foreign.Marshal.Alloc 
import Foreign.Ptr 

data Struct 

foreign import ccall "header.h get_struct" 
    get_struct :: Ptr Struct -> IO() 

main = alloca $ \ptr -> get_struct ptr 

GHC는 Storable Struct의 인스턴스가 없다고 말하면서 컴파일하지 않습니다. 내가 직접 구현할 수 :

instance Storable Struct where 
    sizeOf _ = ... 
    alignment _ = ... 

을하지만 그 목적을 격파에 근접 - 내가 구조체에의하든 상관하지 않는 경우와 같은 일을 정의하고 싶지 않아요.

Ptr 클래스가 Storable이기 때문에 포인터에 대한 포인터가 정상적으로 작동하는 것으로 나타났습니다. 그래서 나는 get_struct를 호출하기 전에 ptrpeek를 사용하여 목표로하고있는 무슨을 수행 할 수 있습니다 이것은 해킹 같은 느낌

main = alloca $ \ptr -> do 
    ptr <- peek ptr 
    get_struct ptr 

을하지만.

인스턴스를 정의하지 않고 빈 데이터 선언을 Storable으로 간주 할 수있는 방법이 있습니까?

+2

이것은 해킹입니다. 내부 포인터를위한 공간을 할당하지 않는다; 방금 임의의 메모리를 가리키고 있습니다. 이 방법은 segfaults 거짓말. –

+0

그래서 합법적으로 포인터에 대한 포인터를 원한다면, 나는 두 개의'alloca' 호출을 사용하고 다른 하나는'poke' 포인터를 사용해야합니다, 맞습니까? – zmthy

+0

예 (다른 형태의 할당을 사용하십시오). 포인터에 대한 포인터의 가장 일반적인 용도는 포인터 아웃 값입니다.이 경우 바인드 된 함수가 할당을 만들고 문자열 배열을 사용합니다 ('argv'라고 생각하십시오). Th –

답변

6

얼마나 큰지 모르는 경우 할당 할 수 없습니다. 함수가 인수를 무시하려고합니까? 그런 다음 널 포인터를 전달하십시오. 그렇지 않으면 실제로 구조체에 충분한 공간을 할당해야합니다. 즉, 0 바이트 또는 포인터 크기의 버퍼를 할당하여 모서리를 자르지 마십시오. 호출 된 함수가 버퍼의 끝을 지나서 메모리를 손상 시키므로 작성합니다.

데이터 선언을 끝내거나 적절한 크기 및 정렬 값을 사용하여 Storable 인스턴스를 작성하십시오. 어떤 형태로든 크기/정렬 데이터를 제공 할 방법이 없습니다.

+2

이 목적을 위해 chs와 같은 편리한 도구를 사용할 수 있습니다.이 값을 알아내는 데 도움이됩니다. – fuz

+3

인스턴스의 모든 메소드를 정의 할 필요는 없습니다. 구조체를 하스켈에 마샬링하고 싶지 않다면'sizeOf'와'alignment' 만 정의하고'peek'과'poke'는 정의하지 않은 채로 (또는 error를 호출하십시오.) –

2

다음은 또 다른 접근 방법입니다. 할당해야하는 객체를 정의하는 모든 C 헤더 파일에 액세스 할 수 있다고 가정합니다. 사실이라면 C 객체를 할당하고 해제하기위한 C 코드의 얇은 레이어를 작성할 수 있습니다. 여러분의 하스켈 코드는 하스켈 코드가 포인터 뒤에 무엇이 있는지를 알 필요없이 이러한 C 함수를 호출 할 수 있습니다. 하스켈은 또한 하스켈의 가비지 컬렉터가 객체가 더 이상 필요 없다는 것을 알고있을 때 자동으로 무료 코드를 호출 할 수 있습니다.