3.3. C++ ABI and type factory infrastructure¶
3.3.1. Cast¶
When C++ interfaces with daScript, cast ABI is followed.
by value types are converted to and from vec4f, in specific memory layout
by reference types have their address converted to and from vec4f
It is expected that vec4f * can be prune to by value type by simple pointer cast, becase daScript interpreter will in certain cases access pre-cast data via v_ldu intrinsic:
template <typename TT>
TT get_data ( vec4f * dasData ) { // default version
return cast<TT>::to(v_ldu((float *)dasData));
}
int32_t get_data ( vec4f * dasData ) { // legally optimized version
return * (int32_t *) dasData;
}
ABI infrastructure is implemented via C++ cast template, which servers two primary functions
casting
from
C++ to daScriptcasting
to
C++ from daScript
The from
function expects daScript type as an input, and outputs vec4f.
The to
function expects vec4f, and outputs daScript type.
Lets review the following example:
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)); }
};
It implements ABI for the int32_t, which packs int32_t value at the beginning of vec4f using multiplatform intrinsics.
Lets review example, which implements default packing of by reference type:
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)); }
};
Here pointer to the data is packed in vec4f using multiplatform intrinsics.
3.3.2. Type factory¶
When C++ types are exposed to daScript, type factory infrastructure is employed.
To expose any custom C++ type, use MAKE_TYPE_FACTORY
macro,
or MAKE_EXTERNAL_TYPE_FACTORY
and IMPLEMENT_EXTERNAL_TYPE_FACTORY
macro pair:
MAKE_TYPE_FACTORY(clock, das::Time)
Example above tells daScript that C++ type das::Time will be exposed to the daScript with the name clock.
Lets look at implementation of MAKE_TYPE_FACTORY
macro:
#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; } \
}; \
};
What happens in the example above is that two templated policies are exposed to C++.
typeName
policy has single static function name
, which returns string name of the type.
typeFactory
policy creates a smart pointer to daScript das::TypeDecl type, which corresponds to C++ type.
It expects to find type somewhere in the provided ModuleLibrary (see Modules).
3.3.3. Type aliases¶
Custom type factory is preferable way to create aliases:
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"; } };
In the example above C++ application already has Point3 type, which is very similar to daScript float3. Exposing C++ functions which operate on Point3 is preferable, so implementation creates an alias named Point3 which corresponds to das Type::tFloat3.
Sometimes custom implementation of typeFactory
is be required to expose C++ to daScript
type in a more native fashion. Lets review the following example:
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;
}
};
Here C++ type SomeVariant matches daScript variant type with its memory layout. The code above exposes C++ type alias and creates corresponding TypeDecl.