【问题标题】:Passing a string to a C library from OCaml using Ctypes and Foreign使用 Ctypes 和 Foreign 从 OCaml 将字符串传递给 C 库
【发布时间】:2013-12-31 03:44:09
【问题描述】:

我真的是 OCaml 的新手,我想尝试使用 pcap 做一些工作作为入门的一种方式,只是,似乎没有维护它的库。在看了这本很棒的 Real World OCaml 书之后,我想我会尝试编写一个绑定。

这是(糟糕的)代码 sn-p:

open Ctypes
open Foreign
open PosixTypes
let char_ptr = "                 "

let pcap_lookupdev = foreign "pcap_lookupdev" (string @-> returning string_opt)

let result = pcap_lookupdev char_ptr

let test2 =
    match result with
    | None -> char_ptr
    | Some str -> str
 ;;

print_string test2;;

pcap_lookupdev 函数返回包含设备名称的字符串或空指针。那一点似乎工作正常(尽管我知道我的代码几乎不是惯用的)。

在用 C 语言编写时,您需要提供一个字符数组来保存任何错误消息。所以如果返回一个空指针,你应该失败,原因保存在这个字符数组中。此字符数组的长度应为“PCAP_ERRBUF_SIZE”。但是我无法弄清楚两件事:

  1. 如何从 C 库中提取该常量大小并创建一个该大小的字符串
  2. 将字符串正确传递给函数,以便正确填充错误消息

非常感谢任何帮助:)

【问题讨论】:

    标签: ocaml ctypes pcap


    【解决方案1】:

    对于 1) 将 #ifdef'd 符号导入 OCaml 的最简单方法是编写一个 C 程序,该程序输出一个带有这些符号值的单独模块。然后,当您需要符号时,您只需在绑定中使用此模块。您可以在here 找到这种方法的示例。

    对于 2) 我想说 ctypes 的 string 有点欺骗性,因为它似乎没有双向作用,也就是说,您应该只将它用于 const char * 或返回类型。在这种情况下,您需要使用字符数组,然后将其转换为字符串(此 char_array_as_string 函数应添加到 ctypes I think)。这是完整的示例,请注意,在 ctypes 的未来版本中,Array 模块将其名称更改为 CArray

    (* Compile with: ocamlfind ocamlopt -package ctypes.foreign -linkpkg -cclib -lpcap \
                                        -o test.native test.ml *)
    
    open Ctypes;;
    open Foreign;;
    
    module Pcap : sig
      val lookupdev : unit -> [ `Ok of string | `Error of string ]
    end = struct
    
      let errbuf_size = 256 (* N.B. This should not be hardcoded, see 1) above *)
    
      let char_array_as_string a =
        let len = Array.length a in 
        let b = Buffer.create len in 
        try 
          for i = 0 to len -1 do 
            let c = Array.get a i in 
            if c = '\x00' then raise Exit else Buffer.add_char b c
          done;
          Buffer.contents b 
        with Exit -> Buffer.contents b
    
      let lookupdev = 
        foreign "pcap_lookupdev" (ptr char @-> returning string_opt)
    
      let lookupdev () =
        let err = Array.make char ~initial:'\x00' errbuf_size in
        match lookupdev (Array.start err) with 
        | None -> `Error (char_array_as_string err)
        | Some dev -> `Ok dev
    
    end
    
    let test () = match Pcap.lookupdev () with 
    | `Ok dev -> Printf.printf "dev: %s\n" dev
    | `Error err -> Printf.printf "error: %s\n" err
    
    let () = test ()
    

    【讨论】:

    • 感谢您提供如此详细而完整的答案 - 如果没有您的帮助,我不可能得到这个。谢谢,新年快乐! ;)
    猜你喜欢
    • 1970-01-01
    • 2021-04-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-12
    • 2017-07-08
    • 1970-01-01
    • 2011-02-11
    相关资源
    最近更新 更多