【问题标题】:Parsing template tuple parameters in D解析D中的模板元组参数
【发布时间】:2023-04-05 22:29:01
【问题描述】:

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

【问题讨论】:

    标签: templates tuples d


    【解决方案1】:

    就像你说的,这是一种 hack,你应该避免不必要的 hack。

    一个(明显的)解决方案是将存储顺序移到维度之前,尽管我猜您想使用该默认参数。

    要解决这个问题,您可以为行和列专业创建特定的模板:

    // 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;
    }
    

    然后您可以在用户代码中使用TensorRowOrderTensorColumnOrder,或者当您需要通用T_storageOrder 时只使用Tensor

    【讨论】:

    • 这确实可行,但我认为这仍然是一个黑客,因为这是一个幸运的镜头,存储订单只需要 2 个值,因为这是一个幸运的镜头,我可以用尺寸来区分它,感谢枚举。此外,我还必须创建另一个别名来定义默认存储顺序,以防止用户在大多数情况下甚至不需要知道它时明确地处理它,但是我必须创建我打算编写的每个子类的这 3 个别名......我认为这不太好。
    【解决方案2】:

    仅供参考,这就是我最终要做的。

    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;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多