【问题标题】:pass an Image from c++ to Haskell and get a string back将图像从 c++ 传递给 Haskell 并返回一个字符串
【发布时间】:2014-02-28 12:41:54
【问题描述】:

我想从 c++ 中调用一个带有图像作为参数的 Haskell 函数。它只是一个无符号字符数组,包含有关宽度和高度(以像素为单位)的信息。

到目前为止,我有这个工作代码。

-- Stuff.hs

module Stuff where

import Data.List
import Data.Word
import qualified Data.Vector.Unboxed as V

import Foreign.Ptr
import Foreign.Storable
import Foreign.C.Types
import Foreign.C.String
import Foreign.Marshal.Array
import Foreign.Marshal.Alloc

foreign export ccall freeResult :: CString -> IO ()
foreign export ccall doWithImageStruct :: ImageStruct -> IO CString

data Image = Image Word32 Word32 (V.Vector Double)

type ImageStruct = Ptr ImageStructType

-- CUInt is Word32.
-- https://hackage.haskell.org/package/base-4.6.0.0/docs/Foreign-C-Types.html#t:CInt
data ImageStructType = ImageStructType CUInt CUInt (Ptr CUChar)

instance Storable ImageStructType where
  sizeOf _ = 12
  alignment = sizeOf
  peek ptr = do
    w <- peekByteOff ptr 0
    h <- peekByteOff ptr 4
    p <- peekByteOff ptr 8
    return (ImageStructType w h p)

imageStructTypeToImage :: ImageStructType -> IO Image
imageStructTypeToImage (ImageStructType (CUInt width) (CUInt height) p) = do
  pixelsCUChar <- peekArray (fromIntegral $ width * height) p
  let pixels = map (\(CUChar c) -> fromIntegral c) pixelsCUChar
  return $ Image width height (V.fromList pixels)

doWithImage :: Image -> String
doWithImage (Image w h p) =
  intercalate " " [show w, show h, show $ V.sum p]

doWithImageStruct :: ImageStruct -> IO CString
doWithImageStruct is = do
  imageStruct <- peek is
  image <- imageStructTypeToImage imageStruct
  newCString $ doWithImage image

freeResult :: CString -> IO ()
freeResult s = free s

// StartEnd.c
#include <Rts.h>

void HsStart()
{
   int argc = 1;
   char* argv[] = {"ghcDll", NULL}; // argv must end with NULL

   // Initialize Haskell runtime
   char** args = argv;
   hs_init(&argc, &args);
}

void HsEnd()
{
   hs_exit();
}

它编译

ghc -Wall -O2 -outputdir build -shared -o build\Stuff.dll Stuff.hs StartEnd.c

cpp 部分 (MSVC 2010) 如下所示:

// main.cpp
// link with /OPT:NOREF

#pragma comment(lib,"Stuff.dll.a")
#include "HsFFI.h"
#include "Stuff_stub.h"
#include <cstdint>
#include <iostream>
#include <string>
#include <vector>

extern "C" {
    void HsStart();
    void HsEnd();
}

struct Image {
    Image( std::uint32_t w, std::uint32_t h, std::uint8_t* p )
        : width_( w ), height_( h ), pixels_( p )
    {}
    std::uint32_t width_;
    std::uint32_t height_;
    std::uint8_t* pixels_;
};

int main()
{
    using namespace std;

    HsStart();

    // create image
    const uint32_t width = 320;
    const uint32_t height = 240;
    vector<uint8_t> mem( width * height, 10 );
    mem[1] = 13;
    Image image( width, height, &mem[0] );

    // Send Image to Haskell and receive a String.
    auto resultPtr = doWithImageStruct( &image );
    string result( reinterpret_cast<char*>( resultPtr ) );
    freeResult( resultPtr );

    cout << result << "\n";

    HsEnd();
}

输出如预期:

320 240 768003.0

我的问题是:这是正确的方法吗?或者它现在没有崩溃而实际上我有未定义的行为只是纯粹的运气?

编辑:我修复了上面的代码,以便为该线程的未来读者展示固定位宽整数的正确用法。

【问题讨论】:

  • 注意:“这是正确的方法吗?” 通常可以很好地表明这个问题属于codereview.stackexchange.com
  • @Zeta:我首先在这里发布它,因为我问的是正确性,而不是风格。但是好的,如果没有人在这里回答,我会在那里发布。谢谢。
  • 我注意到您使用的是 Int 和 int,并假设它们都是 4 个字节。问题是这两者都是平台定义的,并且不能保证您的 Haskell 和 C 编译器都会以这种方式定义它们。我建议切换到明确命名的位长度。 Haskell 在 Data.Word 和 Data.Int 中有这些。
  • 哦,谢谢。我会的。

标签: c++ arrays pointers haskell ffi


【解决方案1】:

我建议你使用C->Hs 来生成Storable ImageStructType 实例。其他一切看起来都不错。

【讨论】:

    猜你喜欢
    • 2013-07-22
    • 1970-01-01
    • 2015-08-07
    • 2017-10-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-12
    • 2013-05-03
    相关资源
    最近更新 更多