d与C++互操作

原文

为什么是D?

强类型系统编程语言
原型立即投入生产
最佳C++集成,出色的C集成

支持的特征

几乎所有东西
class/struct,ref,指针,const,nothrow...
模板(!)
重载运算符(!!)
异常(!!!)

第0步,组织

+agora
|-dub.json
|-source/agora
|-source/scpd
|-source/scpp

第一步:构建系统

    "preGenerateCommands": [
        "$DUB --verbose --single scripts/build_scpp.d"
    ],
    "sourceFiles-posix": [
        "source/scpp/build/*.o"
    ],
    "sourceFiles-windows": [
        "source/scpp/build/*.obj"
    ],

    "versions": [ "_GLIBCXX_USE_CXX98_ABI" ],
    "dflags": [ "-extern-std=c++17" ],
    "lflags-posix": [ "-lstdc++" ],

第 2 步:了解目标

//目标:
CppRuntime_Clang => OSX, Linux, Windows
CppRuntime_Gcc => Linux (OSX in the future?)
CppRuntime_Microsoft => Windows

第3步:简单的东西

extern(C++) struct Foo { int a; }
extern(C++) void func1 (ref const(Foo) f);
extern(C++) void func2 (const(Foo*) f);
extern(C++) void func3 (const(Foo**) f);
//
struct Foo { int a; };
void func1 (Foo const& f);
void func2 (Foo const* f);
// void func2 (Foo const* const f);
void func3 (Foo const* const* f);

D代码:遵循D规则

名字空间:

extern(C++, "dlang", "awesome", "app") void awesomeFunc ();
// 不要这样:
extern(C++, dlang.awesome.app) void lessAwesome ();
static assert(        lessAwesome.mangleof ==
    dlang.awesome.app.lessAwesome.mangleof);

更灵活的名字空间:

version (CppRuntime_Clang)
    enum StdNamespace = AliasSeq!("std", "__1");
else
    enum StdNamespace = "std";

// 注意括号
public extern(C++, (StdNamespace)) struct equal_to (T = void) {}

简单方便:

public extern(C++, (StdNamespace)) struct pair (T1, T2)
{
    T1 first;
    T2 second;
}

注意:混杂|虚表/偏移|大小|生命期函数(ctor/dtor/copy/move).
混杂pragma(mangle, str).虚表/偏移|大小通过测试,生命期ref/指针/包装器.

测试大小

static foreach (Type; GlueTypes)
    extern(C++) ulong cppSizeOf (ref Type);

/// 检查大小
unittest
{
    foreach (Type; GlueTypes)
    {
        Type object = Type.init;
        assert(Type.sizeof == cppSizeOf(object),
            format("'%s'的类型大小不匹配: %s (D) != %s (C++)",Type.stringof, Type.sizeof, cppSizeOf(object)));
    }
}

测试布局

/// 构内包含字段的`大小/偏移`.
extern(C++) struct FieldInfo { long size, offset; }

static foreach (Type; GlueTypes)
    extern(C++) FieldInfo cppFieldInfo (ref Type, const(char)*);

再来:

/// 检查C++构/对象的大小和布局
unittest
{
    foreach (Type; TypesWithLayout)
    foreach (idx, field; Type.init.tupleof) {
        auto object = Type.init;
        auto field_info = cppFieldInfo(object,
            Type.tupleof[idx].stringof.toStringz);

        assert(typeof(field).sizeof == field_info.size,format("'%s'的'%s'字段大小不匹配: %s (D) != %s (C++)",
                Type.tupleof[idx].stringof, Type.stringof,
                typeof(field).sizeof, field_info.size));

        assert(Type.tupleof[idx].offsetof == field_info.offset,
            format("'%s'的'%s'的偏移不匹配: %s (D) != %s (C++)",
                Type.tupleof[idx].stringof, Type.stringof,
                Type.tupleof[idx].offsetof, field_info.offset));
    }
}

标::映射

#include <map>

template<typename K, typename V>
class Map {
    static Map<K,V>* make ()     { return new Map<K,V>(); }

    V& operator[] (K const& key) { return this->map[key]; }

    void insertOrAssign(const K& key, const V& value) {
        this->map.insert_or_assign(key, value);
    }

    std::map<K, V> map;
};

// 显式实例化
template struct Map<char const*, int>;

标::映射D端:

extern(C++, class):
struct Map (Key, Value) {
    extern(D) void opIndexAssign (Value value, const Key key)
    {
        this.insertOrAssign(key, value);
    }

    static Map* make ();
    ref Value opIndex (ref const Key key);
    private void insertOrAssign(const ref Key, const ref Value);
}

应该变这样:

#include <map>
template class std::map<char const*, int>;
//变为
import core.stdcpp.map;
alias MyMap = map!(const(char)*, int);

绑定std

当前core.stdcpp,将转移到另一个.
allocator, array, vector, string, exception, memory, string_view...,但无map.

C++包装器代码

序号优点
1你需要它(来实例化模板)
2你最强大的盟友
3ref传递
4奇怪的C++代码:包装throw,返回值等…

逐步替换C++

替换单个函数很容易,替换方法也很简单,清除依赖/提高代码质量的简单方法:

整合C++的特征

extern(C++, [class|struct])
extern(C++, ident|expression)
core.attributes : gnuAbiTag
pragma(mangle, str_or_decl, [str])
__traits(getTargetInfo, "something")

及,复制构造器/内部指针(串),及DWARF异常处理.

哪些有用

序号有用点
1良好的C++代码
2开始就有的额外C++代码
3显式模板实例化或包装器
4ref/指针传递
5手工制作,及基本用户类型
6-preview=in(constT&)
7不使用DMD
posted @   zjh6  阅读(82)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示