使用Ctypes和Foreign将字符串从OCaml传递到C库

时间:2013-12-31 03:44:10

标签: ocaml ctypes pcap

我是OCaml的新手,并且想尝试使用pcap作为一种入门方式,但是,似乎没有一个维护的库。在看了一本很棒的真实世界OCaml书之后,我想我会写一个有约束力的书。

这是(可怜的)代码段:

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. 的字符串
  3. 将字符串正确传递给函数,以便正确填充错误消息
  4. 感谢任何帮助,非常感谢:)

1 个答案:

答案 0 :(得分:8)

对于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 ()