You can make a Storable instance for ConnGraph
, but it's a bit dodgy. The usual pattern is to malloc space for an array and marshal to that. However, since you know the max size, and space is allocated in the struct, you can take advantage of this and write:
newtype ConnGraph = ConnGraph {unConnGraph :: [CShort]}
instance Storable ConnGraph where
sizeOf _ = maxval*sizeOf (undefined :: CShort)
alignment _ = alignment (undefined :: CShort)
poke ptr (ConnGraph lst) = if length lst <= maxval
then pokeArray (castPtr ptr) lst
else error "Can't poke ConnGraph, too big!"
I'm not entirely happy with this, it seems fragile. If you ever want to marshal a ConnGraph
outside of the inchi_Atom
struct, it may cause problems. If you do go this route, I think it's important to make ConnGraph
a newtype because this definition won't interfere with any other values of type [CShort]
.
Instead of creating this Storable instance, you could use hsc2hs's #offset
macro to determine the starting position, then use pokeArray on the incremented pointer. This actually looks easier:
pokeArray (ptr `plusPtr` (#offset inchi_Atom, neighbor)) $ unConnGraph atoms'
you should probably check that the length doesn't exceed maxval
here.
The reason for the error on the nullPtr
line is because you left out the ptr
argument. However, this code isn't strictly correct. If your pointer type is larger than short
, you'll write the nullPtr
value into several fields. Better would be to explicitly clear the whole array. The final instance will be
-- I don't know how to do CPP in hsc2hs-generated Haskell, but you could create a separate module
-- that defines maxval = MAXVAL
maxval = 20
instance Storable INCHIAtom where
sizeOf _ = (#size inchi_Atom)
alignment _ = alignment (undefined :: CDouble)
peek _ = error "peek is not implemented"
poke ptr (INCHIAtom atoms' label' bondType' charge') =
do
(#poke inchi_Atom, x) ptr $ (0 ::CDouble)
(#poke inchi_Atom, y) ptr $ (0 ::CDouble)
(#poke inchi_Atom, z) ptr $ (0 ::CDouble)
(#poke inchi_Atom, neighbor) ptr $ atoms'
(#poke inchi_Atom, bond_type) ptr $ bondType'
(#poke inchi_Atom, bond_stereo) ptr $ ConnGraph (replicate maxval 0)
(#poke inchi_Atom, elname) ptr $ label'
(#poke inchi_Atom, num_bonds) ptr $ (length $ unConnGraph atoms')
(#poke inchi_Atom, num_iso_H) ptr $ (0 :: CSChar)
(#poke inchi_Atom, isotopic_mass) ptr $ (0 :: CShort)
(#poke inchi_Atom, radical) ptr $ (0 :: CSChar)
(#poke inchi_Atom, charge) ptr $ charge'
I also changed the alignment to match CDouble
. Structs are aligned to be at least as strict as the strictest member, which in this case is double
. This may or may not be the same alignment as int
, depending on your system.