简体   繁体   English

haskell FFI传入和传出C结构数组

[英]haskell FFI passing array of C structs in and out

I am trying to perform a series of transforms on graphical files using Haskell and Repa/DevIL. 我正在尝试使用Haskell和Repa / DevIL对图形文件执行一系列转换。 The starting example used was provided by the Haskell wiki page https://wiki.haskell.org/Numeric_Haskell:_A_Repa_Tutorial . Haskell Wiki页面https://wiki.haskell.org/Numeric_Haskell:_A_Repa_Tutorial提供了使用的起始示例。 I am an imperative programmer of 30 years experience with some erlang for good measure, trying to learn Haskell outside a classroom environment. 我是一位具有30年经验的命令式程序员,从某种程度上讲,他很努力地尝试在课堂环境之外学习Haskell。

The problem is manipulating the data after the file load was first transformed into a Repa array: 问题是在文件加载首次转换为Repa数组后处理数据:

import Data.Array.Repa.IO.DevIL (runIL,readImage,writeImage,Image(RGB),IL)
import qualified Data.Array.Repa as R
import Data.Vector.Unboxed as DVU
import Control.Monad

main :: IO ()
main = do
  [f] <- getArgs
  (RGB a) <- runIL $ Data.Array.Repa.IO.DevIL.readImage f
  let
    c = (computeP (R.traverse a id rgbTransform)) :: IL (Array U DIM3 Float)

which is successfully cast to type "Array F DIM3 Float" as output from the rgbTransform. 成功将其转换为类型“ Array F DIM3 Float”,作为rgbTransform的输出。 From that point on it has been a nightmare to use the data. 从那时起,使用数据一直是一场噩梦。 Flicking the array storage type between F(oreign) and U(nboxed) changes all following call's usability, plus the Repa-added monad layer IL forces use of liftM for nearly every equation following the 1st transform: 在F(oreign)和U(nboxed)之间轻拂数组存储类型会更改所有后续调用的可用性,加上添加Repa的monad层IL会强制对一次转换后的几乎每个方程使用liftM:

  let -- continued
     sh = liftM R.extent c -- IL DIM3
     v = liftM R.toUnboxed c -- IL (Vector Float)
     lv = liftM DVU.length v -- IL Int
     f = liftM indexed v -- vector of tuples: (Int,a) where Int is idx
     k = (Z :. 2) :. 2 :. 0 :: DIM3

These are the routines I can call without error. 这些是我可以正确调用的例程。 The IO monad's print command produces no output if placed in or after this 'let' list, due to the IL monad layer. 由于IL monad层的缘故,如果将IO monad的print命令放在此“ let”列表中或之后,则不会产生任何输出。

The game plan for the curious: 好奇的游戏计划:

  1. read the graphic file (done, via Repa) 读取图形文件(通过Repa完成)
  2. resize image (not done, no resize in Repa, must be hand-coded) 调整图像大小(未完成,在Repa中未调整大小,必须手工编码)
  3. transform and convert image from Word8 to Float (done) 将图像从Word8转换并转换为Float(完成)
  4. get a Stablepointer to the transformed Float data (not done) 获取转换后的Float数据的稳定指针(未完成)
  5. transform in-place the Float data as an array of C structs of {Float a,b,c;}, by an external C routine via FFI (not completely done). 通过外部C例程通过FFI将{Float a,b,c;}的C结构数组原位转换为Float数据(未完全完成)。 This is done hopefully without marshalling a new graphic array by passing a pointer to the data 希望通过传递指向数据的指针而无需编组新的图形数组来完成此操作
  6. perform more passes over the transformed data to extract more info (partly done). 对转换后的数据执行更多传递以提取更多信息(部分完成)。

I am looking for help with issues 4 and 5. 我正在寻求有关问题4和5的帮助。

4 -> The type system has been difficult to deal with while attempting to get C-usable memory pointers. 4->在尝试获取C可用的内存指针时,很难处理类型系统。 Going thru the mountains of haskell library calls has not helped. 穿过山顶的Haskell图书馆电话没有帮助。

5 -> The external C routine is of type: 5->外部C例程的类型为:

foreign import ccall unsafe "transform.h xform"
c_xform :: Ptr (CFloat,CFloat,CFloat) ->
           CInt ->
           IO ()

The Ptr is expected to point to an unboxed flat C array of rgb_t structs: Ptr预计指向rgb_t结构的拆箱平面C数组:

typedef struct
{
    float r;
    float g;
    float b;
} rgb_t;

Available web-based FFI descriptions of how to deal with array pointers in FFI are non-existent if not downright obscure. 如果不是很晦涩的话,关于如何在FFI中处理数组指针的基于Web的FFI可用描述就不存在了。 The fairly straightforward idea of unfreezing and passing in a C array of floating-point RGB structs, modifying them in-place and then freezing the result is what I had in mind. 取消冻结并传递C数组的浮点RGB结构,就地修改它们,然后冻结结果的想法非常简单。 The external transform is pure in the sense that the same input will produce predictable output, does not use threads, does not use global vars nor depend upon obscure libraries. 外部转换是纯净的,因为相同的输入将产生可预测的输出,不使用线程,不使用全局var也不依赖于晦涩的库。

Foreign.Marshal.Array seems to provide a way to convert haskell data to C data and other way around. Foreign.Marshal.Array似乎提供了一种将haskell数据转换为C数据的方法。

I tested interfacing C code and haskell using the following files (Haskell + FFI for the first time for me) 我使用以下文件测试了接口C代码和haskell的配置(对我来说是首次Haskell + FFI)

hsc2hs rgb_ffi.hsc
ghc main.hs rgb_ffi.hs rgb.c

rgb.h rgb.h

#ifndef RGB_H
#define RGB_H

#include <stdlib.h>

typedef struct {
    float r;
    float g;
    float b;
} rgb_t;

void rgb_test(rgb_t * rgbs, ssize_t n);

#endif

rgb.h rgb.h

#include <stdlib.h>
#include <stdio.h>
#include "rgb.h"

void rgb_test(rgb_t * rgbs, ssize_t n)
{
    int i;

    for(i=0; i<n; i++) {
        printf("%.3f %.3f %.3f\n", rgbs[i].r, rgbs[i].g, rgbs[i].b);
        rgbs[i].r *= 2.0;
        rgbs[i].g *= 2.0;
        rgbs[i].b *= 2.0;
    }
}

rgb_ffi.hsc rgb_ffi.hsc

{-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE CPP                      #-}

module RGB where

import Foreign
import Foreign.C
import Control.Monad (ap)

#include "rgb.h"

data RGB = RGB {
      r :: CFloat, g :: CFloat, b :: CFloat
} deriving Show

instance Storable RGB where
    sizeOf    _ = #{size rgb_t}
    alignment _ = alignment (undefined :: CInt)

    poke p rgb_t = do
      #{poke rgb_t, r} p $ r rgb_t
      #{poke rgb_t, g} p $ g rgb_t
      #{poke rgb_t, b} p $ b rgb_t

    peek p = return RGB
             `ap` (#{peek rgb_t, r} p)
             `ap` (#{peek rgb_t, g} p)
             `ap` (#{peek rgb_t, b} p)

foreign import ccall "rgb.h rgb_test" crgbTest :: Ptr RGB -> CSize -> IO ();

rgbTest :: [RGB] -> IO [RGB]
rgbTest rgbs = withArray rgbs $ \ptr ->
               do
                 crgbTest ptr (fromIntegral (length rgbs))
                 peekArray (length rgbs) ptr

rgbAlloc :: [RGB] -> IO (Ptr RGB)
rgbAlloc rgbs = newArray rgbs

rgbPeek :: Ptr RGB -> Int -> IO [RGB]
rgbPeek rgbs l = peekArray l rgbs

rgbTest2 :: Ptr RGB -> Int -> IO ()
rgbTest2 ptr l =
    do
      crgbTest ptr (fromIntegral l)
      return ()

main.hs main.hs

module Main (main) where

import RGB

main =
 do
    let a = [RGB {r = 1.0, g = 1.0, b = 1.0},
             RGB {r = 2.0, g = 2.0, b = 2.0},
             RGB {r = 3.0, g = 3.0, b = 3.0}]
    let l = length a
    print a
    -- b <- rgbTest a
    -- print b

    c <- rgbAlloc a
    rgbTest2 c l
    rgbTest2 c l
    d <- rgbPeek c l
    print d
    return ()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM