• DLang 与 C 语言交互(一)


    DLang 与 C 语言交互

    很难受的是,这部分的文档太少了,根本没有 教程向 的文章。所以我写了此文以做分享。

    本文原址链接(防止机器搬运):https://www.cnblogs.com/jeefy/p/17499441.html

    阅读提示:请保证如下条件:

    • 会基本C语言使用,以及其编译命令。

    • 会基本D语言使用,以及其编译命令。

    • 会使用 Makefile 之类的东西(不会也无所谓),不会 dub

    最简单的例子

    // chello.c
    #include 
    
    void hello(void) {
        puts("Hello World!");
    }
    
    // hello.d
    extern (C) void hello();
    
    void main() {
        hello();
    }
    

    其实上面的两个程序的意义非常明显,就是最基本的 Hello World! 输出罢了。

    我们有了上面两个文件后可以通过如下命令编译:

    gcc -c chello.c
    dmd hello.d chello.o
    

    最终会得到一个 main 可执行文件(也可能是 main.exe,看系统)。运行它,你就得到了 Hello World!


    接下来对于部分做出解释。

    1. 函数的声明:在 hello.d 中有一句 extern (C) void hello();,这就是函数的声明部分,由于 dlang 与 C++ 一样在处理函数名的时候会做一些变化,然而 C 中的函数名却是不变的,所以需要显式声明其函数名的处理方式:extern (C),也就是按照 C 的处理方式处理,这样才能调用到 chello.o 中的 hello 方法。

    2. 编译的命令:唯一需要注意的是需要把 .o 文件显示的放入编译命令中。

    3. 头文件:这是困扰我最久的一个点,dlang 如何使用 C 的头文件?后来才发现了一个误区,dlang 不存在头文件的说法,也就是说 dlang 无法 直接 使用 .h 文件。但是我们又需要声明函数怎么办?在官方文档中有这样一个命令:

      gcc -E -P program.h > program.lst
      

      这个命令的作用在于列出所有声明的东西(包括函数与结构体的声明)。在经过一定的修改后,就可以变为 .d 文件(做类似于头文件的作用)。


    接下来我们尝试一点更加高级的东西。

    链接库

    我使用的例子是我自己在写的一个小东西。参见:Jeefy / jimg · GitLab

    假如我需要使用一个简单的 SDL 程序以显示一个色块。我可以很轻易的写出如下代码:

    // cshow.c
    #include 
    
    #include 
    
    // 省略了部分宏定义....避免冗长
    
    int showColor(int R, int G, int B) {
        SDL_PREWORK(50, 50);
        while (!done) {
            SDL_MYEQUIT(60);
    
            if (SDL_SetRenderDrawColor(ren, R, G, B, 255) < 0) {
                fprintf(stderr, "Error Set render draw color: %s\n", SDL_GetError());
                return -1;
            }
            SDL_RenderFillRect(ren, NULL);
            SDL_RenderPresent(ren);
        }
    
        SDL_CLEANUP;
        return 0;
    }
    

    对于 dlang 中的调用也很明了:

    // color.d
    
    // show the color by SDL (written in C)
    extern (C) int showColor(int, int, int);
    

    只是问题出在编译的部分。

    如果我们按照一下步骤编译:

    gcc -c cshow.c `pkg-config sdl2 --cflags --libs`
    dmd color.d cshow.o
    

    我们最终会发现出现 undefined reference to ... 的错误。

    在官方文档中并没有提及使用系统链接库的问题。但是 隐晦 的给出了解决方法。

    我们直接找到 libSDL2.so 所在的位置。在我的系统中是 /usr/lib/x86_64-linux-gnu/libSDL2.so

    于是编译命令新增一个部分,变为:

    gcc -c cshow.c `pkg-config sdl2 --cflags --libs`
    dmd color.d cshow.o /usr/lib/x86_64-linux-gnu/libSDL2.so
    

    也就是直接把链接库也带上……属实给我整无语了。

    啊,于是,我们不能真的吧所有库的位置全部找到,然后在编译的时候一个一个复制上去吧。所以就需要类似与 Make 或者 CMake, meson 之类的构建工具辅助我们。这部分不过多展开。


    函数,参数?

    有些时候,我们想要在 C 中使用 dlang 分配的内存。如:

    // exmaple.c
    void fillMem(int *dst, int size, int val) {
        for (int i = 0; i < size; ++i)
            dst[i] = val;
    }
    

    自然的,我们可以想到:

    // example.d
    // 错误示例!!!!!!!!!!!!!!!!!!!!
    import std.stdio;
    
    extern (C) void fillMem(int[] dst, int size, int val);
    
    void main() {
        int[] arr = new int[](3);
        fillMem(arr, 3, 1);
        writeln(arr[2]);
    }
    

    可以编译通过,然后运行……输出 0

    讲道理应该是输出 1 才对。

    那么很明显,类型出了问题,我们不应该如此操作。

    于是参考官方文档。正确的姿势如下:

    import std.stdio;
    extern (C) void fillMem(int* dst, int size, int val);
    
    void main() {
        int[] arr = new int[](3);
        fillMem(arr.ptr, 3, 1);
        writeln(arr[0]);
        writeln(arr[1]);
        writeln(arr[2]);
        writeln(arr.length);
    }
    

    为什么如此?在 dlang 中,可变长数组其实有两个变量,可以理解为:

    struct {
        int length;
        int * ptr;
    }
    

    当然,类型肯定不是这样的……

    也就是说 arr.ptr 中才存着数据。所以如此。

    那么考虑不可变长数组。好像还是只能使用上面那种声明和调用方法……

    这就是在传递参数的时候最需要注意的一个点。其他的参数类型转化可以参考:

  • 相关阅读:
    java计算机毕业设计web家庭财务管理系统源码+mysql数据库+系统+lw文档+部署
    分享:身份证读卡器java工程乱码解决办法
    Eginx配置(SSL,令牌登录认证,IP白名单,代理转发)
    2023最新SSM计算机毕业设计选题大全(附源码+LW)之java基于的药护到家系统1z4ia
    手写数字识别简化版——0和1的二分类问题
    10个月1000 Star、62位贡献者,我们都做对了什么?| 2023年度总结
    linux网络问题常用命令
    Spring框架概述 --- 控制反转, 依赖注入, 容器和Bean
    1-5-10 快恢在数字化安全生产平台 DPS 中的设计与落地
    CentOS中的安装Docker
  • 原文地址:https://www.cnblogs.com/jeefy/p/17501476.html