将C数组指针包装为Chapel数组的最佳方法

时间:2018-12-17 18:40:21

标签: chapel

与C进行互操作时,我经常发现自己被赋予了指向数组的指针。 Chapel当前让我将此指针视为1D 0索引数组。但是,在某些情况下,我想将此指针视为Chapel数组(例如具有多维域)。在教堂中,最惯用的方式是什么?

我可能试图通过将C指针包装在一个类(具有一个域)中,并定义thisthese(串行和并行)方法来做到这一点,以便可以索引和遍历全班。为了实现这一点,具有将域中的索引映射到0索引位置的功能将很有用。有这样的内置功能吗?

3 个答案:

答案 0 :(得分:4)

不幸的是,似乎没有一个适用于每个域的功能。 DefaultRectangularArr在幕后使用了称为getDataIndex的方法来执行此计算,看起来其他数组类型依赖于内部存储类上定义的类似方法。看起来这些在域本身上不可用。我怀疑依靠这些方法都是不明智的,因为无论如何它们可能会作为实施调整的一部分而更改。

我们希望最终可以使用为互操作性定义的makeArrayFromPtr函数之类的东西将最终描述的指针包装在Chapel数组中。不幸的是,该功能目前仅支持1D 0索引数组,但目前正在努力扩展我们对数组互操作性的支持。我希望该函数能够调整其参数或为多维数组定义另一个版本,我们仍在弄清楚这一点。

答案 1 :(得分:4)

我很好奇我是否可以毫不费力地欺骗Chapel数组引用C中分配的缓冲区。我能够做到,但对结果并不感到骄傲。具体来说:

  • 它使用的功能不是面向用户的,因此将来可能会更改或中断
  • 当前仅适用于在每个维度中从0开始索引的矩形Chapel数组

请记住这些注意事项,这是一个简单的C标头,其中公开了一些从Chapel调用的简单例程。第一个分配一个平凡的9元素数组;第二个释放了它的论点。

#include <stdlib.h>

double* getDataPtr() {
  double* dataPtr = (double*)malloc(9*sizeof(double));

  dataPtr[0] = 1.1;
  dataPtr[1] = 1.2;
  dataPtr[2] = 1.3;
  dataPtr[3] = 2.1;
  dataPtr[4] = 2.2;
  dataPtr[5] = 2.3;
  dataPtr[6] = 3.1;
  dataPtr[7] = 3.2;
  dataPtr[8] = 3.3;

  return dataPtr;
}

void freeDataPtr(double* ptr) {
  free(ptr);
}

这是调用它的Chapel代码,然后将C指针强制插入具有适当大小和从0开始的索引的现有数组:

//                                                                        
// Declare a Chapel array.  Note that this program will only work as      
// written if it uses 0-based indexing.                                   
//                                                                        
var A: [0..2, 0..2] real;

//                                                                        
// testit.h is the C code above.  It defines a simple C stub that returns a pointer
// to floating point data.                                                            
//                                                                        
require "testit.h";

//                                                                        
// Here are the key routines that testit.h exposes back to Chapel to      
// get and free a pointer to floating point data.                         
//                                                                        
extern proc getDataPtr(): c_ptr(real);
extern proc freeDataPtr(ptr: c_ptr(real));

//                                                                        
// Grab the pointer from C                                                
//                                                                        
const myCPtr = getDataPtr();

//                                                                        
// Save two pointer values defined in A's descriptor.  Note that these    
// are not part of its public interface, so are not recommended for       
// typical users and could change / break at any future point.            
//                                                                        
const saveData = A._value.data;
const saveShiftedData = A._value.shiftedData;

//                                                                        
// Replace these pointers with the one we got from C.                     
//                                                                        
A._value.data = (myCPtr: _ddata(real));
A._value.shiftedData = (myCPtr: _ddata(real));

//                                                                        
// print out A, "proving" that we're referring to the data from C         
//                                                                        
writeln(A);

//                                                                        
// restore the original pointers to avoid having Chapel try to free       
// the C memory / leak the Chapel memory.                                 
//                                                                        
A._value.data = saveData;
A._value.shiftedData = saveShiftedData;

//                                                                        
// Free the C data                                                        
//                                                                        
freeDataPtr(myCPtr);

Chapel writeln(A)语句的输出为:

1.1 1.2 1.3
2.1 2.2 2.3
3.1 3.2 3.3

我认为向Chapel的GitHub issues page提出功能请求是完全合理的,该提议提出了一种更好的面向用户的界面来采用这样的C指针,尽管这是一种更好且更正式的方式。

答案 2 :(得分:1)

下面可能有一种略有不同的方法。这里的缺点是这不是Chapel数组,但确实允许索引/迭代,就像Chapel数组一样。这无法处理跨步或非零对齐,并且没有进行边界检查。

prototype module CPtrArray {

  use SysCTypes;

  record CPtrArray {
    type eltType;
    param rank : int;
    const first : rank*int;
    const blk : rank*int;

    var data : c_ptr(eltType);


    proc init(type t, D : domain, ptr : c_ptr(t)) {
      this.eltType = t;
      this.rank = D.rank;
      this.first = D.first;
      var blktmp : rank*int;
      blktmp(rank) = 1;
      if (rank > 1) {
        for param idim in (rank-1)..1 by -1 {
          blktmp(idim) = blktmp(idim+1)*D.shape(idim+1);
        } 
      }
      this.blk = blktmp;
      this.complete();
      data = ptr;
    }

    proc getDataOffset(ndx : rank*int) : int {
      var offset = ndx(rank)-first(rank);
      if (rank > 1) {
        for param idim in 1..(rank-1) do
          offset += (ndx(idim)-first(idim))*blk(idim);
      }
      return offset;
    }

    inline proc this(ndx : rank*int) ref {
      return data[getDataOffset(ndx)];
    }

    inline proc this(i:int ...rank) ref {
      return this(i);
    }

    // Should provide iterators as well.                 
  }

}

使用它的简单测试程序:

use CPtrArray;

var A : [0.. #10] real;
forall ii in 0.. #10 do A[ii] = ii+1.0;

writeln(A);

var ptr = c_ptrTo(A[0]);

// Code for testing the array class goes here
const D = {1.. #5, 3.. #2};
var A1 = new CPtrArray(real, D, ptr);

for ndx in D {
  writeln(ndx," ",A1[ndx]);
}