Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best way to wrap a C array pointer as a Chapel array

Tags:

chapel

When interoperating with C, I often find myself being handed a pointer to an array. Chapel currently lets me treat this pointer as a 1D 0-indexed array. However, there are cases where I'd like to treat this pointer as a Chapel array (with eg. a multidimensional domain). What would the most idiomatic way to achieve this in Chapel?

I might have tried to do this by wrapping the C pointer in a class (with a domain) and defining this, and these (serial and parallel) methods so that one could index into and iterate over the class. In order to implement this, it would be useful to have a function that maps an index in a domain to a 0-indexed location. Is there such a built in function?

like image 631
Nikhil Padmanabhan Avatar asked Dec 23 '22 02:12

Nikhil Padmanabhan


2 Answers

Unfortunately there does not appear to be a function that works for every domain. DefaultRectangularArr uses a method called getDataIndex under the covers that performs this computation, and it looks like other array types rely on a similar method defined on an inner storage class. It looks like these are not available on the domains themselves. I suspect relying on any of these would be inadvisable as they may be changed as part of an implementation adjustment, anyways.

Our hope is that eventually pointers like what you are describing could be wrapped in Chapel arrays using something like the makeArrayFromPtr function defined for interoperability. Unfortunately this function only supports 1D 0-indexed arrays today, but work is currently being done to expand our support for array interoperability. I would expect that function to adjust its arguments or for another version to be defined for multi-dimensional arrays, and we are still figuring that out.

like image 63
Lydia Duncan Avatar answered Jan 07 '23 10:01

Lydia Duncan


I was curious whether I could trick a Chapel array into referring to a buffer allocated in C without much effort. I was able to do it, but am not proud of the result. Specifically:

  • it uses features that aren't user-facing, so may change or break at any future point
  • it currently only works for rectangular Chapel arrays that start indexing at 0 in each dimension

With those caveats in mind, here's a simple C header that exposes a few simple routines to be called from Chapel. The first allocates a trivial 9-element array; the second frees its argument.

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

and here's the Chapel code that calls into it, then forces the C pointer into an existing array of the appropriate size and 0-based indices:

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

The output of Chapel's writeln(A) statement is:

1.1 1.2 1.3
2.1 2.2 2.3
3.1 3.2 3.3

I think it'd be completely reasonable to file a feature request on Chapel's GitHub issues page proposing a better user-facing interface for adopting a C pointer like this, albeit in a nicer and more official way.

like image 31
Brad Avatar answered Jan 07 '23 10:01

Brad