Hi to everybody,
I am trying to improve my bindings to CFITSIO, a C library to read/write files in the FITS format. FITS files are containers that can include tables as well as images (i.e. 2-D matrices).
There is a family of functions, fits_read_col???, which allow to read one column from a table into an array. The prototype is complex, but it essentially requires the first and last row number in the table to read, and a pointer to an array (which must have been already allocated). For some complicated reasons that have to do with the file format, the most efficient way to read a column is to call fits_read_col multiple times and read the column in chunks.
At the moment I am therefore trying to write a Nim binding to fits_read_col, let's call it readColumn. My question is: what is the best method to use to pass the address of the first element in the array that will receive the data read from the file? Suppose that the table contains 4 rows:
+---+ | 1 | +---+ | 2 | +---+ | 3 | +---+ | 4 | +---+
Suppose that in this case the best way to read this column is to call readColumn twice: the first time it should read the first two rows, the second time the two rows left at the end. One possibility would be to implement readColumn as (this example is simpler than the reality, as readColumn is going to accept much more parameters):
proc readColumn(f : var FitsFile, firstIdx : int, lastIdx : int, dest : ptr float64)
Then I would call the procedure like this:
var rows : seq[int]
readColumn(f, 1, 2, addr(rows[0]))
readColumn(f, 3, 4, addr(rows[2]))
However, I find the use of addr quite ugly. Is there a better, more Nim-style way to implement readColumn? I thought of the possibility of passing the start index as an additional parameter of readColumn:
proc readColumn(f : var FitsFile, firstIdx : int, lastIdx : int, dest : openArray[float64], destIdx : int)
# ...
var rows : seq[int]
readColumn(f, 1, 2, rows, 0)
readColumn(f, 3, 4, rows, 2)
But I don't like it very much, as the true readColumn already has 7 parameters! What I really like to do is something like this:
proc readColumn(f : var FitsFile, firstIdx : int, dest : openArray[float64])
# ...
var rows : seq[int]
readColumn(f, 1, rows[0..2])
readColumn(f, 3, rows[3..4])
But I tried to do so, and found that the compiler complains about rows[0..2] and rows[3..4]. What is the best way to implement what I have in mind?
template `{}`(a, i): expr = addr(a[i])
readColumn(f, 1, rows{0})
Thanks a lot, Araq, and thanks also for having found a much cleaner and shorter way to express what I needed (using a slice as an l-value).
I have one more question regarding this. Within readColumn sometimes I need to iterate over the destination array, in order to apply some transformation to the data that have just been read from the file. But if I go through the addr route, how can I treat the pointer to the array as a real array? In C, pointers can be used as arrays; but is this the same for Nimrod?
Hi! Glad you're working on the CFITSIO wrapper - I used the original Fortran version in undergrad and while my opinion of Fortran 2003 has improved, I'll be a happy person if I never maintain any FORTRAN-77 code again.
I don't think that Nim allows for C-style pointer arithmetic, but what you could do is (after getting a ref/ptr to the array), then dereference it with the [] operator with something like:
var myArrayFromRef: array[length,int] = (doublePtr)[]
this should be the equivalent of:
int totSize = 100;
int **myArrayPtr = (int *) calloc((size_t) num_elements, (size_t) num_elements * totSize);
...
int myArray[] = (*myArrayPtr);