3.3. C++ ABI 和类型工厂基础结构

3.3.1. Cast

当 C++ 与 Daslang 交互时,将遵循 cast ABI。

  • 值类型在特定的内存布局中与 vec4f 相互转换

  • 引用类型的地址与 vec4f 相互转换

预计 vec4f * 可以通过简单的指针强制转换被修剪为 by 值类型,因为在某些情况下,Daslang 解释器将通过 v_ldu 内部函数访问预转换数据:

template <typename TT>
TT get_data ( vec4f * dasData ) {           // 默认版本
    return cast<TT>::to(v_ldu((float *)dasData));
}

int32_t get_data ( vec4f * dasData ) {      // 合法优化的版本
    return * (int32_t *) dasData;
}

ABI 基础设施是通过 C++ 强制转换模板实现的,该模板有两个主要功能:

  • from C++ 转换为 Daslang

  • 从 Daslang 强制转换 to C++

from 函数需要一个 Daslang 类型作为输入,并输出一个 vec4f。

to 函数需要一个 vec4f,并输出一个 Daslang 类型。

让我们回顾一下以下示例:

template <>
struct cast <int32_t> {
    static __forceinline int32_t to ( vec4f x )            { return v_extract_xi(v_cast_vec4i(x)); }
    static __forceinline vec4f from ( int32_t x )          { return v_cast_vec4f(v_splatsi(x)); }
};

它实现了 int32_t 的 ABI,它使用多平台内联函数在 vec4f 的开头打包了一个 int32_t 值。

让我们回顾另一个示例,该示例实现引用类型的默认打包:

template <typename TT>
struct cast <TT &> {
    static __forceinline TT & to ( vec4f a )               { return *(TT *) v_extract_ptr(v_cast_vec4i((a))); }
    static __forceinline vec4f from ( const TT & p )       { return v_cast_vec4f(v_splats_ptr((const void *)&p)); }
};

在这里,指向数据的指针使用多平台内部函数打包在 vec4f 中。

3.3.2. 类型工厂

当 C++ 类型公开给 Daslang 时,将采用类型工厂基础结构。

要公开任何自定义C++类型,请使用 MAKE_TYPE_FACTORY 宏,或者 MAKE_EXTERNAL_TYPE_FACTORYIMPLEMENT_EXTERNAL_TYPE_FACTORY 宏对:

MAKE_TYPE_FACTORY(clock, das::Time)

上面的示例告诉 Daslang,C++ 类型`das::Time`将公开给名称为`clock`的 Daslang。

让我们看看 MAKE_TYPE_FACTORY 宏的实现:

#define MAKE_TYPE_FACTORY(TYPE,CTYPE) \
    namespace das { \
    template <> \
    struct typeFactory<CTYPE> { \
        static TypeDeclPtr make(const ModuleLibrary & library ) { \
            return makeHandleType(library,#TYPE); \
        } \
    }; \
    template <> \
    struct typeName<CTYPE> { \
        constexpr static const char * name() { return #TYPE; } \
    }; \
    };

在上面的示例中发生的情况是,两个模板化策略公开给 C++。

typeName 策略有一个静态函数 name,它返回类型的字符串名称。

typeFactory 策略创建一个指向 Daslang 的智能指针 das::TypeDecl 类型,它对应于 C++ 类型。 它期望在提供的 ModuleLibrary 中的某个位置找到类型(参阅 Modules)。

3.3.3. 类型别名

自定义类型工厂是创建别名的首选方法:

struct Point3 { float x, y, z; };

template <>
struct typeFactory<Point3> {
    static TypeDeclPtr make(const ModuleLibrary &) {
        auto t = make_smart<TypeDecl>(Type::tFloat3);
        t->alias = "Point3";
        t->aotAlias = true;
        return t;
    }
};

template <> struct typeName<Point3>   { constexpr static const char * name() { return "Point3"; } };

在上面的示例中,C++ 应用程序已经具有`Point3`类型,这与 Daslang 的 float3 非常相似。 最好公开对 Point3 进行作的 C++ 函数,因此该实现会创建一个名为 Point3 的别名,该别名对应于 das Type::tFloat3。

有时,需要一个 typeFactory 的自定义实现,以便以更原生的方式将 C++ 暴露给 Daslang 类型。让我们回顾一下以下示例:

  struct SampleVariant {
      int32_t _variant;
      union {
          int32_t     i_value;
          float       f_value;
          char *      s_value;
      };
  };

template <>
struct typeFactory<SampleVariant> {
    static TypeDeclPtr make(const ModuleLibrary & library ) {
        auto vtype = make_smart<TypeDecl>(Type::tVariant);
        vtype->alias = "SampleVariant";
        vtype->aotAlias = true;
        vtype->addVariant("i_value", typeFactory<decltype(SampleVariant::i_value)>::make(library));
        vtype->addVariant("f_value", typeFactory<decltype(SampleVariant::f_value)>::make(library));
        vtype->addVariant("s_value", typeFactory<decltype(SampleVariant::s_value)>::make(library));
        // optional validation
        DAS_ASSERT(sizeof(SampleVariant) == vtype->getSizeOf());
        DAS_ASSERT(alignof(SampleVariant) == vtype->getAlignOf());
        DAS_ASSERT(offsetof(SampleVariant, i_value) == vtype->getVariantFieldOffset(0));
        DAS_ASSERT(offsetof(SampleVariant, f_value) == vtype->getVariantFieldOffset(1));
        DAS_ASSERT(offsetof(SampleVariant, s_value) == vtype->getVariantFieldOffset(2));
        return vtype;
    }
};

此处,C++ 类型 SomeVariant 与 Daslang 变体类型及其内存布局匹配。 上面的代码公开了一个 C++ 类型别名,并创建了一个对应的 TypeDecl。