我在D中为线性代数库实现了一个多维张量,这基本上就是我为基类所针对的:
class Tensor( T_scalar, T_dimensions ..., int T_storageOrder = StorageOrder.columnMajor )
{
}
在这个想法中,用户可以通过模板参数定义张量的特征,因此我可以在编译时推导出尽可能多的东西,有点像Eigen。 不幸的是,编译器对该定义不满意并触发错误,例如:
Tensor(T_scalar,T_args...,int T_storageOrder = StorageOrder.columnMajor) template tuple parameter must be last one
我不太清楚为什么会出现这种限制,但我最终做了我认为是黑客攻击的...基本上,将StorageOrder定义为枚举允许我检查模板元组的最后一个参数参数匹配枚举中的一个值,如果是这样,我可以用它来设置该张量的StorageOrder值,否则我用默认值设置它。
enum StorageOrder : int
{
columnMajor = -1,
rowMajor = -2
}
class Tensor( T_scalar, T_args ... )
{
private:
alias TensorTraits!( T_scalar, T_args ) traits;
alias traits.dimensions T_dimensions;
alias traits.storageOrder T_storageOrder;
}
struct TensorTraits( T_scalar, T_args ... )
if ( areTemplateParametersValid!( T_scalar, T_args )() )
{
static immutable auto dimensions = mixin( extractDataFromTemplateTupleParameter.dimensions );
static immutable int storageOrder = extractDataFromTemplateTupleParameter.storageOrder;
private:
static auto extractDataFromTemplateTupleParameter()
{
Tuple!( string, "dimensions", int, "storageOrder" ) templateTupleParameterData;
static if ( T_args[$ - 1] == StorageOrder.columnMajor || T_args[$ - 1] == StorageOrder.rowMajor )
{
alias TypeTuple!( T_args[0 .. $ - 1] ) dimensionsTuple;
templateTupleParameterData.storageOrder = T_args[$ - 1];
}
else
{
alias TypeTuple!( T_args ) dimensionsTuple;
templateTupleParameterData.storageOrder = StorageOrder.columnMajor;
}
static assert( dimensionsTuple.length > 0,
"No dimensions have been defined." );
foreach ( dimension; dimensionsTuple )
{
static assert( isIntegral!( typeof( dimension ) ),
"Dimensions sizes needs to be defined as integrals." );
static assert( dimension >= 0,
"Dimensions sizes cannot be negative." );
}
templateTupleParameterData.dimensions = dimensionsTuple.stringof;
return templateTupleParameterData;
}
}
static bool areTemplateParametersValid( T_scalar, T_args ... )()
{
static assert( isNumeric!( T_scalar ),
"The 'T_scalar' template argument is not a numeric type." );
static assert( T_args.length > 0,
"No dimensions have been defined." );
return true;
}
因为我刚开始使用D,并且由于我对这个黑客不太确定,我想知道这对你们这些听起来是否合适,或者是否有更好的处理方法?
答案 0 :(得分:1)
就像你说的,这是一个黑客,你应该避免在不必要的地方乱砍。
一个(显而易见的)解决方案是在维度之前移动存储顺序,尽管我猜你想要使用该默认参数。
要解决此问题,您可以创建行和列专业的特定模板:
// Generic Tensor with storage order before dimensions.
class Tensor( T_scalar, int T_storageOrder, T_dimensions... )
{
}
template TensorRowOrder( T_scalar, T_dimensions... )
{
alias Tensor( T_scalar, StorageOrder.rowMajor, T_dimensions ) TensorRowOrder;
}
template TensorColumnOrder( T_scalar, T_dimensions... )
{
alias Tensor( T_scalar, StorageOrder.columnMajor, T_dimensions ) TensorColumnOrder;
}
然后,您可以在用户代码中使用TensorRowOrder
或TensorColumnOrder
,或在需要通用Tensor
时使用T_storageOrder
。
答案 1 :(得分:0)
仅供参考,这是我最终做的事情。
class Array( T_scalar, T_args ... )
{
private:
alias ArrayTraits!( T_scalar, T_args ) traits;
alias traits.isDynamic T_isDynamic;
alias traits.shapeAtCompileTime T_shapeAtCompileTime;
alias traits.sizeAtCompileTime T_sizeAtCompileTime;
alias traits.storageOrder T_storageOrder;
alias traits.dataType T_dataType;
}
struct ArrayTraits( T_scalar, T_args ... )
if ( areTemplateParametersValid!( T_scalar, T_args )() )
{
private:
static if ( hasFlag( Flags.storageOrder ) )
alias T_args[0 .. $ - 1] shapeTuple;
else
alias T_args shapeTuple;
public:
static immutable bool isDynamic = hasFlag( Flags.dynamic ) ? true : false;
static immutable auto shapeAtCompileTime = getShapeAtCompileTime();
static immutable size_t sizeAtCompileTime = getSizeAtCompileTime();
static immutable StorageOrder storageOrder = hasFlag( Flags.storageOrder ) ?
T_args[$ - 1] : defaultStorageOrder;
static if ( hasFlag( Flags.dynamic ) == true )
alias T_scalar[] dataType;
else
alias T_scalar[sizeAtCompileTime] dataType;
public:
static auto getShapeAtCompileTime()
{
static if ( hasFlag( Flags.dynamic ) == true )
{
static assert( shapeTuple.length == 1,
"The shape of a dynamic array needs to be defined at run-time." );
size_t[1] shapeAtCompileTime = [Storage.dynamic];
return shapeAtCompileTime;
}
else
{
static assert( shapeTuple.length > 0,
"No dimensions have been defined." );
size_t[shapeTuple.length] shapeAtCompileTime;
foreach ( i, dimension; shapeTuple )
{
static assert( isIntegral!( typeof( dimension ) ),
"Dimensions sizes for a static array needs to be defined as integrals." );
static assert( dimension > 0,
"Dimensions sizes for a static array cannot be null or negative." );
shapeAtCompileTime[i] = dimension;
}
return shapeAtCompileTime;
}
}
static size_t getSizeAtCompileTime()
{
if ( hasFlag( Flags.dynamic ) == true )
return 0;
size_t size = 1;
foreach ( dimension; shapeAtCompileTime )
size *= dimension;
return size;
}
private:
/++ Parses the template tuple parameter to extract the different flags passed, if any. +/
static int getFlags()
{
int flags = 0;
if ( is( typeof( T_args[0] ) == Storage ) && T_args[0] == Storage.dynamic )
flags |= Flags.dynamic;
if ( is( typeof( T_args[$ - 1] ) == StorageOrder ) )
flags |= Flags.storageOrder;
return flags;
}
/++ Checks if the template tuple parameter contains a specific flag. +/
static bool hasFlag( Flags flag )
{
return (getFlags() & flag) == 0 ? false : true;
}
private:
enum Flags : int
{
dynamic = 1 << 0,
storageOrder = 1 << 1
}
}
bool areTemplateParametersValid( T_scalar, T_args ... )()
{
static assert( T_args.length > 0,
"No dimensions have been defined." );
return true;
}