2011-01-25 2 views
8

Je tente de créer un wrapper Haskell pour une bibliothèque C. Les structures sous-jacentes sont trop compliquées pour être exprimées en tant que types explicites, et je ne les utilise pas autrement que pour passer d'une fonction C à l'autre, j'utilise donc EmptyDataDecls pour que GHC puisse les résoudre. Ce dont j'ai besoin est un pointeur vers un de ces types de données, mais quand je tente d'en créer un avec alloca il se plaint que les données ne sont pas du type Storable. Par exemple:Déclaration de données vides pouvant être stockées

{-# 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 ne compile pas, en disant il n'y a pas d'exemple pour Storable Struct. Je pourrais mettre en œuvre moi-même:

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

Mais qui se rapproche de l'encontre du but - Je ne veux pas avoir à définir de telles choses si je ne me soucie pas ce qui est dans le struct. J'ai remarqué qu'un pointeur sur un pointeur fonctionne bien, car la classe Ptr est Storable. Je peux donc accomplir ce que je vise en utilisant peek sur ptr avant d'appeler get_struct:

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

Cela se sent comme un hack, cependant.

Existe-t-il un moyen de prendre en compte les déclarations de données vides Storable sans définir d'instance?

+2

Ceci est un hack. Vous n'allouez jamais d'espace pour le pointeur interne; vous pointez simplement sur une mémoire aléatoire. De cette façon, se trouve le segfaults. –

+0

Donc, si je veux légitimement un pointeur sur un pointeur, je devrais utiliser deux appels 'alloca' et ensuite' poke' un pointeur dans l'autre, non? – zmthy

+0

oui (ou utilisez une autre forme d'allocation). Les utilisations les plus courantes pour un pointeur sur un pointeur sont des valeurs de pointeur, auquel cas la fonction liée fait l'allocation, et des tableaux de chaînes (pensez 'argv'). Th –

Répondre

6

Vous ne pouvez pas attribuer quelque chose si vous ne savez pas quelle est sa taille. La fonction va-t-elle simplement ignorer son argument? Passez ensuite un pointeur nul. Sinon, vous devez allouer suffisamment d'espace pour la structure - ne pas couper les coins en allouant un tampon de taille zéro ou pointeur, car alors la fonction appelée écrira après la fin de votre tampon, corrompant la mémoire. Soit terminer la déclaration de données, soit écrire une instance Storable avec des valeurs de taille et d'alignement appropriées; il n'y a aucun moyen de fournir des données de taille/d'alignement sous une forme ou une autre.

+2

Vous pouvez utiliser un outil comminient comme chs à cet effet - il vous aide à déterminer ces valeurs. – fuz

+3

Notez que vous n'avez pas besoin de définir toutes les méthodes d'une instance. Si vous ne voulez pas rassembler les structures dans Haskell, définissez seulement 'sizeOf' et' alignment' mais laissez 'peek' et' poke' undefined (ou erreur d'appel). –

2

Voici une autre approche qui pourrait fonctionner pour vous. Je suppose que vous avez accès à tous les fichiers d'en-tête C qui définissent les objets que vous devez allouer. Si c'est vrai, vous pouvez écrire une couche mince de code C pour allouer et libérer les objets C. Votre code Haskell peut alors appeler ces fonctions C sans que le code Haskell ait jamais besoin de savoir ce qu'il y a derrière les pointeurs. Haskell peut également appeler automatiquement le code libre lorsque le garbage collector de Haskell sait que les objets ne sont plus nécessaires.

Questions connexes