To: J3 11-132 From: R. Bader Subject: examples for use of the CFI_associate() function Date: 2011 January 28 References: N1838 INTRODUCTION: The function CFI_associate() is designed for two usage patterns: (1) to modify C descriptors for assumed shape entities so as to allow production of array slices / subobjects or create assumed-shape entities inside C, (2) to perform something much (but not fully?) like pointer association for C descriptors for Fortran POINTERs. This paper provides some examples for the use of this function (hopefully making the correct assumptions about its semantics). EDITS to N1838: [21:17+] Add line "A.2.1 Example for array processing using the C descriptor" [21:32+] At the end of A.2. add "A.2.2 Example for creating an array slice in C Given the Fortran interface for a function which is intended to set every second array element, beginning with the first one, to some provided value, interface subroutine set_odd(int_array, val) bind(c) use, intrinsic :: iso_c_binding, only : c_int integer(c_int) :: int_array(:) integer(c_int), value :: val end subroutine end interface the implementation in C reads #include "ISO_Fortran_binding.h" void set_odd(CFI_cdesc_t *int_array, int val) { CFI_bounds_t bounds[1]; int status; bounds[0]->lower_bound = int_array->dim[0].lower_bound; bounds[0]->upper_bound = int_array->dim[0].lower_bound + int_array->dim[0].extent bounds[0]->stride = 2; /* the above is equivalent to saying int_array(1::2) in Fortran */ status = CFI_associate(int_array, int_array->base_addr, bounds); set_all(int_arr, val); } The function implements its functionality in terms of a Fortran subprogram subroutine set_all(int_array, val) bind(c) integer(c_int) :: int_array(:) integer(c_int), value :: val int_array = val end subroutine Invocation of set_odd() from a Fortran program is done as follows: integer(c_int) :: d(5) d = (/ 1, 2, 3, 4, 5 /) call set_odd(d, -1) write(*, *) d The program will print -1 2 -1 4 -1 During execution of the subprogram set_all(), its dummy object int_array would appear to be an array of size 3 with lower bound 1 and upper bound 3. It is also possible to invoke set_odd() from C. However, this requires some care: Firstly, the above call to CFI_associate() irreversibly modifies the descriptor {\footnote At least the extent and sm members of int_array->dim[0] will be modified: sm will be doubled, and the value of the extent member will be changed to (extent + 1)/2. <> }. Hence, it is necessary to generate a new descriptor before any invocation of set_odd() and use that as an argument if it is desired that the complete object remain available throughout execution of the invoking C code. Secondly, it is the C programmer's responsibility to make sure that all members of the descriptor have the correct value on entry to the function. Inserting additional checking into the function's implementation can alleviate this problem. /* necessary includes omitted */ #define ARRAY_SIZE 5 CFI_cdesc_t *d, *d_copy; CFI_bounds_t bounds[1]; CFI_index_t subscripts[1]; void *base; int i, status; d = CFI_create_cdesc(sizeof(int), 1, CFI_int, CFI_attribute_assumed); base = malloc(ARRAY_SIZE*sizeof(int)); bounds[0]->lower_bound = 1; bounds[0]->upper_bound = ARRAY_SIZE; bounds[0]->stride = 1; /* different from CFI_allocate, stride must be specified */ status = CFI_associate(d, base, bounds); dcopy = CFI_create_cdesc(sizeof(int), 1, CFI_int, CFI_attribute_assumed); status = CFI_associate(dcopy, base, bounds); set_odd(dcopy, -1); /* dcopy is now corrupted and should not be used any more, unless it is refreshed by a renewed call to CFI_associate() */ for (i=1; i<=ARRAY_SIZE; i++) { subscripts[1] = i; printf(" %d",*((int *)CFI_address(d, subscripts))); } printf("\n"); free(base); free(d); free(dcopy); This C program will print (apart from formatting) the same output as the Fortran program above. It also demonstrates how an assumed shape entity is dynamically generated within C. A.2.3 Example for handling objects with the POINTER attribute The following C function modifies a pointer to an integer variable to point at a global variable defined inside C: #include "ISO_Fortran_binding.h" int y[1] = { 2 }; void change_target(CFI_cdesc_t *ip) { CFI_bounds_t bounds[1]; CFI_index_t subscripts[1]; if (ip->attribute == CFI_attribute_pointer) { CFI_associate(ip, y, bounds); } } The following Fortran code use, intrinsic :: iso_c_binding interface subroutine change_target(ip) bind(c) import :: c_int integer(c_int), pointer :: ip end subroutine end interface integer(c_int), target :: it = 1 integer(c_int), pointer :: it_ptr it_ptr => it write(*,*) it_ptr call change_target(it_ptr) write(*,*) it_ptr will then print 1 2" <>