很多语言都可以实现与C语言的互相调用,但我觉得D语言做的更好些,D与C的兼容是二进制兼容,不是在源码上的兼容,也就是说编译手的二进制文件是可以互相链接的。废话不多说,直接上例子


一、在D语言中调用C函数

首先是准备一个C函数

// foo.c

#include 
int test()
{
    printf("message from c\n");
    return 123; // 随便返回一个值
}

$ gcc -c foo.c

生成 foo.o


D代码

// bar.d
import std.stdio;

extern(C) int test();

void main()
{
    int rv = test();
    writeln("test return is:", rv);
}

$ dmd bar.d foo.o

./bar

message from c

test return is:123


可以看到,要调用C的函数只需要使用 extern(C) 声明函数,然后一起编译即可,如果是调用C生成的so,只需要在编译D程序的时候加上链接函数即可如 dmd bar.d -L-lxx

是不是很简单呢,跟C++调用C看上去一样,只是编译的时候D需要的是二进制文件,C++需要的是源代码

网上有很多资料都说D对C的支持是不完全的,这么说虽然也没问题,但是明显好像是说支持的不好,其实支持的非常好,只是不同的编译器对C都会有些各自的扩展,这方面支持的不算好


从D调用C没什么,但是能从C调用D函数要怎么做呢,这方面能作到和C++一样方便的就只有D了,在D语言中分两种情况,一种是简单的函数,另一种是D函数中用到了D中的高级特性,如类,关联数组,委托等,简单函数只要声明为extern(C) 就可以了,如果使用了高级特性也不难,只不过没有这方面的资料来说明,需要调用D运行库中的额外两个函数,rt_init() 和 rt_term ,相信看到名子就能理解他的意思了,要想支持D中的高级特性只需要初始化D运行时库,在结束的时候释放D运行库的资源即可,看下面的例子


D代码

import std.stdio;

class Test // 使用类
{
    int a = 100;
    void printA()
    {   
        writeln(this.a); // 使用D标准IO函数
    }   

    auto getAA()
    {   
        return ["k1":111, "k2": 222, "k3": 334]; // 返回一个关联数组
    }   
}

extern(C) void test() // 需要声明为extern(C)
{
    auto t = new Test;
    t.printA();
    writeln(t.getAA());
}


C代码

// bar.c
int rt_init();
int rt_term();
int test();

int main()
{
    rt_init();
    test();
    // ...
    rt_term();
}


编译

$ dmd -c foo.d

$ gcc -o main bar.c foo.o -lphobos2 -L /usr/share/dmd/lib/

./main

100

["k1":111, "k2":222, "k3":334]

我们看到,上面说到的额外两个函数的用法

需要注意的是编译的时候需要链接 libphobos2这个库,这个是D的标准库,我的是在Mac下的默认安装目录,根据情况找到安装目录即可


与C语言互调除了C++外,只有D做的最简单了,只比C++多了两个额外的函数调用,除了extern(C) 声明外,其它代码就正常写就行了,不需额外的对C的兼容做努力,在C代码中需要多调用两个函数,如果想要C不知道这两个函数的存在,可以把这两个函数的调用放在D代码里调用就行了,即初始化和释放动作自己负责,不给调用者带来麻烦


如果您对这方面的内容还有什么疑惑,欢迎交流