3

我正在为 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,并且由于我对这个 hack 不太确定,我想知道这对你们来说是否听起来不错,或者是否有更好的方法来处理这个问题?

4

2 回答 2

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或,或者仅在需要通用.TensorColumnOrderTensorT_storageOrder

于 2012-09-01T17:20:28.223 回答
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;
}
于 2012-09-22T10:49:35.567 回答