代码之家  ›  专栏  ›  技术社区  ›  Zachary Bechhoefer

ocamlopt:链接期间出错

  •  2
  • Zachary Bechhoefer  · 技术社区  · 6 年前

    我做了这个玩具:

    sql\u连接c:

    // https://dev.mysql.com/doc/refman/8.0/en/mysql-real-connect.html
    // http://zetcode.com/db/mysqlc/
    #include <stdio.h>
    #include <stdlib.h>
    #include <caml/mlvalues.h>
    #include <mysql/mysql.h>
    
    CAMLprim value connect(value dbname, value dbuser, value dbpassword){
        MYSQL *con = mysql_init(NULL);
        mysql_real_connect(con,"localhost",String_val(dbuser), String_val(dbpassword),String_val(dbname),0,NULL,0);
        return (value) con;
    }
    
    CAMLprim value print_query(value con, value tbl, value field, value constraint){
        char query[100];
        sprintf(query, "select * from %s where %s='%s';",(char *) tbl,(char *) field,(char *) constraint);
        mysql_query((MYSQL*) con, query);
    
        MYSQL_RES *result = mysql_store_result((MYSQL*) con);
        int num_fields = mysql_num_fields(result);
        MYSQL_ROW row;
        while ((row = mysql_fetch_row(result))) 
        { 
            for(int i = 0; i < num_fields; i++) 
            { 
                printf("%s ", row[i] ? row[i] : "NULL");
            } 
                printf("\n"); 
        }
        mysql_free_result(result);
        return Val_unit;
    }
    

    主要的ml:

    type dbconn
    
    external print_query: dbconn -> string -> string -> string -> unit = "print_query"
    
    external connect: string -> string -> string -> dbconn = "connect"
    
    let database = "dogs";;
    let user = "root";;
    let pwd = "kafka";;
    
    let tbl = "dogs";;
    let field = "Name";;
    let arg = "reximus";;
    let db = connect database user pwd;;
    print_query db tbl field arg;;
    

    生成文件

    main:
        g++ -c sql_conn.c -lmysqlclient
        ocamlopt main.ml
        ./a.out
    

    并连接(…)工作正常,但我发现了一个错误:

    g++ -c sql_conn.c -lmysqlclient
    ocamlopt main.ml
    main.o: In function `camlMain__entry':
    main.ml:(.text+0x89): undefined reference to `print_query'
    main.o: In function `camlMain__6':
    main.ml:(.data+0xa8): undefined reference to `print_query'
    collect2: error: ld returned 1 exit status
    File "caml_startup", line 1:
    Error: Error during linking
    Makefile:2: recipe for target 'main' failed
    make: *** [main] Error 2
    

    正在制作。感谢您的指点。它说问题在于链接,但当唯一的方法是连接时(…)它工作得很好。我不是一个经验丰富的C程序员(如果你不知道的话),所以也许我遗漏了一些明显的东西。感谢您的指点。

    谢谢你的时间!

    此外,像这样的帖子也应该用mysql标记吗?

    1 回复  |  直到 6 年前
        1
  •  3
  •   Community Egal    4 年前

    首先,你不会因为 connect 因为已经有 connect function in the standard library .我强烈建议重命名您的函数,因为 mysql_real_connect 可能使用标准 连接 如果这样重写它,这将导致无限递归。


    引用自 the OCaml manual :

    如果您使用的是本机代码编译器 ocamlopt 这个 -custom 不需要标记,因为 奥坎洛普 始终生成独立的可执行文件。要构建混合OCaml/C可执行文件,请执行 奥坎洛普 使用以下命令:

    • 所需OCaml本机对象文件的名称( .cmx .cmxa 文件);
    • C对象文件和库的名称( .o ,则, .a ,则, .so .dll 实现所需基本体的。

    And also :

    这个 奥坎洛普 命令的命令行界面非常接近 ocamlc 。它接受相同类型的参数,并在处理完所有选项后按顺序处理它们:

    […]

    • 以结尾的参数 .cmx 被视为编译的目标代码。这些文件与通过编译获得的目标文件一起链接在一起 .ml 参数(如果有)和OCaml标准库,以生成本机代码可执行程序。[…]

    • 以结尾的参数 .c 传递给C编译器,该编译器生成 .o / .obj 对象文件。此对象文件与程序链接。

    以及更高版本:

    -cclib -l libname
    通过 -l libname 链接器的选项。这将导致给定的C库与程序链接。


    免责声明: 实际上,我还没有测试以下步骤。这正是我认为阅读文档有意义的地方。


    因此,我假设正确的编译命令如下所示

    ocamlopt sql_conn.c main.ml -cclib -lmysqlclient
    

    或在生成文件中:

    a.out: sql_conn.c main.ml
            ocamlopt sql_conn.c main.ml -cclib -lmysqlclient
    

    (如果要添加 main 也执行文件的目标,我会执行以下操作

    .PHONY: main
    main: a.out
            ./a.out
    

    )

    如果要将这些步骤分开,则如下所示:

    1. 编写 sql_conn.c 进入 sql_conn.o :

       gcc -c sql_conn.c
      
    2. 编写 main.ml 进入 main.cmx (除其他事项外):

       ocamlopt -c main.ml
      
    3. 将它们链接到一个可执行文件中:

       ocamlopt sql_conn.o main.cmx -cclib -lmysqlclient
      

    或以Makefile形式:

    sql_conn.o: sql_conn.c
            gcc -c sql_conn.c
    
    main.cmx: main.ml
            ocamlopt -c main.ml
    
    a.out: sql_conn.o main.cmx
            ocamlopt sql_conn.o main.cmx -cclib -lmysqlclient
    

    命令问题:

    g++ -c sql_conn.c -lmysqlclient
    

    g++ 选择C++编译器并准备与C++标准库链接。 -lmysqlclient 添加 mysqlclient 库到集合。

    但是 -c 告诉它不要执行链接步骤(因此C++标准库和 mysqlclient 和文件名(以结尾 C )使其切换到C编译器。

    整个事情相当于

    gcc -c sql_conn.c
    

    这会产生 sql\U连接o (包含已编译C代码的对象文件)。

    ocamlopt main.ml
    

    这说明 奥坎洛普 编写 主要的毫升 ,将其与标准OCaml库链接,并生成一个名为 a.out .没有任何东西告诉它去看看 sql\U连接o ,因此该文件将被忽略。

    就是在这里,你得到了一个链接器错误,因为 print_query 未找到(已在中定义 sql\U连接o )。如上所述,您不会收到以下错误 连接 因为系统库中已经存在该名称的符号(虽然带有不兼容的签名,但链接器不知道)。