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_FACTORY
和 IMPLEMENT_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。