We are trying to take over the memory allocation of a legacy Fortran code (+100,000 lines of code) in C++, because we are using a C library for partitioning and allocating distributed memory on a cluster. The allocatable variables are defined in modules. When we call subroutines that use these modules the index seems to be wrong (shifted by one). However, if we pass the same argument to another subroutine we get what we expect. The following simple example illustrates the issue:
hello.f95:
MODULE MYMOD
IMPLICIT NONE
INTEGER, ALLOCATABLE, DIMENSION(:) :: A
SAVE
END MODULE
SUBROUTINE TEST(A)
IMPLICIT NONE
INTEGER A(*)
PRINT *,"A(1): ",A(1)
PRINT *,"A(2): ",A(2)
END
SUBROUTINE HELLO()
USE MYMOD
IMPLICIT NONE
PRINT *,"A(1): ",A(1)
PRINT *,"A(2): ",A(2)
CALL TEST(A)
end SUBROUTINE HELLO
main.cpp
extern "C" int* __mymod_MOD_a; // Name depends on compiler
extern "C" void hello_(); // Name depends on compiler
int main(int args, char** argv)
{
__mymod_MOD_a = new int[10];
for(int i=0; i<10; ++i) __mymod_MOD_a[i] = i;
hello_();
return 0;
}
We are compiling with:
gfortran -c hello.f95; c++ -c main.cpp; c++ main.o hello.o -o main -lgfortran;
Output from running ./main is
A(1): 1
A(2): 2
A(1): 0
A(2): 1
As you can see the output of A is different, though both subroutines printed A(1) and A(2). Thus, it seems that HELLO starts from A(0) and not A(1). This is probably due to that ALLOCATE has never been called directly in Fortran so that it is not aware of the bounds of A. Any work arounds?
The ISO_C_BINDING "equivalent" code:
c++ code:
extern "C" int size;
extern "C" int* c_a;
extern "C" void hello();
int main(int args, char** argv)
{
size = 10;
c_a = new int[size];
for(int i=0; i<size; ++i) c_a[i] = i;
hello();
return 0;
}
fortran code:
MODULE MYMOD
USE, INTRINSIC :: ISO_C_BINDING
IMPLICIT NONE
INTEGER, BIND(C) :: SIZE
TYPE (C_PTR), BIND(C) :: C_A
INTEGER(C_INT), POINTER :: A(:)
SAVE
END MODULE
SUBROUTINE TEST(A)
IMPLICIT NONE
INTEGER A(*)
PRINT *,"A(1): ",A(1)
PRINT *,"A(2): ",A(2)
END
SUBROUTINE HELLO() BIND(C)
USE, INTRINSIC :: ISO_C_BINDING
USE MYMOD
IMPLICIT NONE
CALL C_F_POINTER(C_A,A,(/SIZE/))
PRINT *,"A(1): ",A(1)
PRINT *,"A(2): ",A(2)
CALL TEST(A)
END SUBROUTINE
Output:
A(1): 0
A(2): 1
A(1): 0
A(2): 1
Fortran array dummy arguments always start at the lower bound defined in the subroutine. Their lower bound is not retained during the call. Therefore the argument A in TEST() will always start at one. If you wish it to start from 42, you must do:
INTEGER A(42:*)
Regarding the allocation, you are playing with fire. It is much better to use Fortran pointers for this.
integer, pointer :: A(:)
You can then set the array to point to a C buffer by
use iso_c_binding
call c_f_pointer(c_ptr, a, [the dimensions of the array])
where c_ptr is of type(c_ptr), interoperable with void *, which also comes from iso_c_binding.
---Edit---
Once I see that #Max la Cour Christensen implemented what I sketched above, I see I misunderstood the output of your code. The descriptor was indeed wrong, though I didn't write anything plain wrong. The solution above still applies.
The internal representation of fortran arrays is very different than the one used in C/C++.
Fortran uses descriptors that start with a pointer to the array data, and followed by element type size, number of dimensions, some padding bytes, an internal 32/64 bit byte sequence indicating various flags such as pointer, target, allocatable, can be deallocated, etc. Most of these flags are not documented (at least in ifort that I have worked with), and at the end is a sequence of records, each describing the number of elements in the corresponding dimension, distance between elements, etc.
To 'see' an externally created array from fortran, you'd need to create such descriptors in C/C++, but, it does not end there because fortran also makes copies of them in the startup code of each subroutine before it gets to the first one of your statements, depending on indicators like 'in', 'out, 'inout', and other indicators used in the fortran array declaration.
Arrays within a type declared with specific sizes map well (again in ifort) to corresponding C struct members of the same type and number of elements, but pointer and allocatable type members are really descriptors in the type that need to be initialized to the correct values in all their fields so fortran can 'see' the allocatable value. This is at best tricky and dangerous, since the fortran compiler may generate copy code for arrays in undocumented ways for optimization purposes, but it needs to 'see' all the involved fortran code to do so. Anything coming outise of the fortran domain, is not known and can result in unexpected behavior.
Your best bet is to see if gfortran supports something like iso_c_binding and define such interfaces for your fortran code, and then use iso_c_binding intrinsics to map the C_PTR pointers to fortran pointers to types, arrays, etc.
You can also pass a pointer to a one-dimensional array of char, and its size, and this works for strings mostly as long as the size is passed by value as last argument (again, compiler and compiler-flag dependent).
Hope this helps.
EDIT: changed 'ifort's iso_c_binding' to 'iso_c_binding after Vladimir's comment - thanks!
Related
I am working with a legacy Fortran library that requires a character scalar PATH as an argument to the subroutine. The original interface was:
SUBROUTINE MINIMAL(VAR1, ..., PATH)
CHARACTER (LEN=4096) PATH
...
I need to be able to call this from C++ so I have made the following changes:
SUBROUTINE MINIMAL(VAR1, ..., PATH) &
BIND (C, NAME="minimal_f")
USE ISO_C_BINDING, ONLY: C_CHAR, C_NULL_CHAR
CHARACTER (KIND=C_CHAR, LEN=1), DIMENSION(4096), INTENT(IN) :: PATH
CHARACTER (LEN=4096):: new_path
! Converting C char array to Fortran CHARACTER.
new_path = " "
loop_string: do i=1, 4096
if ( PATH (i) == c_null_char ) then
exit loop_string
else
new_path (i:i) = PATH (i)
end if
end do loop_string
as per this answer. This works to convert the C-style char array to its Fortran scalar equivalent, with two problems:
This code is on the critical path so doing the same conversion every time when the answer is the same is inefficient
I would strongly prefer to not have to edit legacy code
I have tried:
Just accepting a CHARACTER (LENGTH=4096) :: new_path directly with the ISO C binding, but I get the following compiler error:
Error: Character argument 'new_path' at (1) must be length 1 because procedure 'minimal' is BIND(C)
This answer and others that I have read suggest that the ISO C binding seems to restrict what I can pass as parameters to the function, although I haven't found any official documentation yet.
This answer, which gives another algorithm to turn a C-style string
into a Fortran-style equivalent in the C code and passing it to the Fortran subroutine without using the ISO C binding. (This function suggests a similar algorithm). This seems like exactly what I want but I have a linker error without the binding:
Undefined symbols for architecture x86_64:
"_minimal", referenced from:
C++-side function declaration:
extern "C" {
double minimal(int* var1, ..., const char* path);
}
This suggests that my compiler (gcc) prepends the function name with an underscore when in an extern block. gfortran, however, does not let me name the subroutine _minimal so the linker can't find the symbol _minimal. (The aforementioned link suggests adding an underscore to the end of the C-side function name but this doesn't work either because of the leading underscore.)
I want to process a C-style string into a Fortran-style character scalar once in my C++ code and be able to pass it into the original interface. Any ideas?
Fortran 2018 allows interoperable procedures to have character dummy arguments of assumed length, relaxing the restriction that such dummy arguments must be of length one.
So we can write a Fortran procedure as
subroutine minimal(path) bind(c)
use, intrinsic :: iso_c_binding, only : c_char
character(*,c_char), intent(in) :: path
...
end subroutine minimal
and continue our life knowing that we've also improved our Fortran code by using an assumed length scalar instead of an explicit length one. No "Fortran side" copy of this character dummy is required.
The sad part of this story is that the dummy argument path is not interoperable with a char. So instead of the formal parameter of the C (or C++) function being char * , it must be CFI_cdesc_t *. For (C) example:
#include "ISO_Fortran_binding.h"
#include "string.h"
void minimal(CFI_cdesc_t *);
int main(int argc, char *argv[]) {
/* Fortran argument will be a scalar (rank 0) */
CFI_CDESC_T(0) fpath;
CFI_rank_t rank = 0;
char path[46] = "afile.txt";
CFI_establish((CFI_cdesc_t *)&fpath, path, CFI_attribute_other,
CFI_type_char, strlen(path)*sizeof(char), rank, NULL);
minimal((CFI_cdesc_t *)&fpath);
return 0;
}
A C++ example will be similar.
An notable part of the story is that you'll need a Fortran compiler which implements this part of Fortran 2018. GCC 11 does not.
IanH's answer draws attention to an approach which avoids modifying the original Fortran subroutine at all. There certainly are times when avoiding any change there is good (repeating slightly what IanH said):
using bind(c) means an explicit interface will now always be required when calling the modified subroutine through Fortran itself. Perhaps some parts of your code used it with an implicit interface
the original was tested (or wasn't) and you don't want to break anything
you don't want to potentially change the argument from default kind to interoperable kind (if these do differ)
the explicit length dummy argument really is wanted
you just don't want to modify it if not required
Any one of those would make a good argument, so in that spirit I'll add to the C example with the thin wrapper.
Fortran:
subroutine minimal_wrap(path) bind(c, name='minimal')
use, intrinsic :: iso_c_binding, only : c_char
character(*,c_char), intent(in) :: path
call minimal(path)
end subroutine minimal_wrap
subroutine minimal(path)
character(4096) path
print*, trim(path)
end subroutine minimal
C:
#include "ISO_Fortran_binding.h"
#include "string.h"
void minimal(CFI_cdesc_t *);
static const int pathlength=4096;
int main(int argc, char *argv[]) {
/* Fortran argument will be a scalar (rank 0) */
CFI_CDESC_T(0) fpath;
CFI_rank_t rank = 0;
char path[pathlength];
/* Set path as desired. Recall that it shouldn't be null-terminated
for Fortran */
CFI_establish((CFI_cdesc_t *)&fpath, path, CFI_attribute_other,
CFI_type_char, pathlength*sizeof(char), rank, NULL);
minimal((CFI_cdesc_t *)&fpath);
return 0;
}
C++ using containers will arguably be nicer.
Recall that this puts responsibility on the C side to ensure the array is long enough (as you have in pure Fortran calls).
Equally if you need to be robust to differences in default character and interoperable character with that copy (as in IanH's answer) you can apply those same tricks to copy as required (or you can do this with conditional compilation and configure-time checks). By this point however, you may as well just assume always copy or use the array argument.
The answer to the question title is typically a std::string object, padded to the relevant fixed Fortran CHARACTER scalar length with spaces. Alternative storage objects (std::vector<char>, or a C-style char array) could be used on the C++ side, but the approach is similar.
(If the Fortran code used an assumed length character argument, rather than fixed length, then the padding would not be required. Whether this change is possible depends on the details of the MINIMAL subroutine. Fixed length character variables are typically an anachronism - this answer is not advocating their use in new code.)
On the Fortran side, you can write a thin wrapper that the C++ can call, that uses sequence and pointer association to avoid the need to copy the string data, for typical C++/Fortran platforms of today. A copy (or modification of the legacy Fortran code) is unavoidable if the interoperable character kind is not the same as the character kind of the legacy Fortran procedure. The example code below is robust to this situation, but I expect platforms that require that code path to be rare.
For default character and C_CHAR interoperable character arguments, sequence association permits an array dummy argument to be associated with the sequence of characters designated by the actual argument. This effectively permits association between character scalars and arrays with different lengths.
(Do not confuse the ISO_C_BINDING intrinsic module with the BIND(C) procedure suffix. BIND(C) fundamentally changes the interface of a procedure to enable calls between C and Fortran - ISO_C_BINDING is just a module with some handy types, constants and procedures for such calls.)
Example C++:
#include <string>
#include <cassert>
const int path_length = 4096;
extern "C" int legacy_cintf(char* array);
int main()
{
std::string some_long_text
= "It was the best of times, it was the worst of times, it was "
"the age of wisdom, it was the age of foolishness, it was the "
"epoch of belief, it was the epoch of incredulity, it was the "
"season of light, it was the season of darkness, it was the "
"spring of hope, it was the winter of despair.";
assert(some_long_text.size() < path_length);
std::string path = std::string(path_length, ' ');
path.replace(0, some_long_text.size(), some_long_text);
legacy_cintf(&path[0]);
return 0;
}
Example Fortran:
MODULE m
IMPLICIT NONE
CONTAINS
SUBROUTINE legacy_cintf(array) BIND(C, NAME='legacy_cintf')
USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_INT, C_CHAR
CHARACTER(LEN=1,KIND=C_CHAR), TARGET :: array(4096)
CHARACTER(LEN=SIZE(array)), POINTER :: scalar
LOGICAL :: copy_required
copy_required = C_CHAR /= KIND(scalar)
IF (copy_required) THEN
ALLOCATE(scalar)
CALL do_copy(array, scalar)
ELSE
CALL do_associate(array, scalar)
END IF
CALL LEGACY(scalar)
IF (copy_required) DEALLOCATE(scalar)
END SUBROUTINE legacy_cintf
SUBROUTINE do_associate(arg, scalar)
CHARACTER(*), INTENT(OUT), POINTER :: scalar
CHARACTER(LEN=LEN(scalar)), INTENT(IN), TARGET :: arg(1)
scalar => arg(1)
END SUBROUTINE
SUBROUTINE do_copy(arg, scalar)
USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_CHAR
CHARACTER(*), INTENT(OUT) :: scalar
CHARACTER(LEN=LEN(scalar), KIND=C_CHAR), INTENT(IN) :: arg(1)
scalar = arg(1)
END SUBROUTINE do_copy
END MODULE m
SUBROUTINE LEGACY(PATH)
CHARACTER(4096) :: PATH
PRINT *, TRIM(PATH)
END SUBROUTINE LEGACY
I have some functions written in Fortran that take a structure as an argument, but the caller has the data stored in an INTEGER*4(2) array. In order to avoid the copy between the two data structures, I'm wondering if the following implementation of a C++-like reinterpret_cast is valid according to the specification:
STRUCTURE /TimeStamp/
INTEGER*4 secondsSinceEpoch
INTEGER*4 nanos
END STRUCTURE
STRUCTURE /reinterpret_cast/
UNION
MAP
INTEGER*4, POINTER :: array(:)
END MAP
MAP
TYPE (TimeStamp), POINTER :: tstamp
END MAP
END UNION
END STRUCTURE
SUBROUTINE set_time(timeArg)
INTEGER*4, TARGET :: timeArg(2)
RECORD /reinterpret_cast/ time
time % array => timeArg
time % tstamp % secondsSinceEpoch = 12
time % tstamp % nanos = 0
END
Is this implementation of the set_time method guaranteed to work (e.g., set the values of timeArg(1) and timeArg(2))?
No, your function is not guaranteed to work by the Fortran standard and many compilers will refuse the syntax altogether. I am not sure whether Fortran pointers are allowed in the DEC structures and if yes, whether you can union them. They (structure and union and record) were designed before Fortran pointers were put into the standard and are strongly discouraged for new code, but it is quite possible Intel allowed Fortran pointers in allowed them.
Much easier (at least for me) way is to use Fortran standard type(c_ptr) which is basically the C void * pointer.
SUBROUTINE set_time(timeArg)
USE, INTRINSIC :: ISO_C_BINDING
INTEGER(c_int_32), TARGET :: timeArg(2)
type(TimeStamp), POINTER :: tstamp
CALL c_f_pointer(c_loc(timeArg), tstamp)
tstamp % secondsSinceEpoch = 12
tstamp % nanos = 0
END
I also changed the INTEGER*4 because it is also not standard conforming and not guaranteed to be C-interoperable.
Do note that the address of the target dummy argument is valid only in the subroutine unless the actual argument is pointer or target.
What you are looking for is the F90-standard function TRANSFER. It interprets the bit representation of the operand as if it was of the same type of another variable (the "mold"). Thus, this:
USE ISO_FORTRAN_ENV ! For the REALnn and INTnn constants
REAL(REAL32) r
INTEGER(INT32) i
r = 1.0
i = TRANSFER(r, i) ! The second "i" here is unevaluated, just gives the type
Is equivalent to this:
float r = 1.0;
int32_t i;
i = *reinterpret_cast<int*>(&f);
Note that the REALnn and INTnn constants are from Fortran 2008, so your compiler might not have them. I just used them as examples to make sure that the types were compatible, since just like in C, the standard does not say precisely how big a "default real" or "default integer" are.
As an example, I frequently use this function when creating Fortran-based MEX functions in Matlab, since the Matlab interface with Fortran is based on F77 and does not allow you to use pointers to Matlab memory directly, unlike the C interface. I use the TRANSFER function and the ISO_C_BINDING module (F2003) to cast the "integer" (actually a C pointer) Matlab gives me to the Fortran type C_PTR, to a Fortran pointer. Like this:
USE ISO_C_BINDING ! For C_PTR and related functions
INTEGER(INT32), POINTER :: arrayPtr(:)
mwSize n ! This is a type defined in the Matlab-Fortran interface
mwPointer myMatlabArray = ... ! So is this
TYPE(C_PTR) cPtrToData
! Cast the returned C pointer to the data (Matlab interface returns an integer type)
cPtrToData = TRANSFER(mxGetData(myMatlabArray), cPtrToData)
! Since Fortran arrays/pointers have size information, get the length
n = mxGetNumberOfElements(myMatlabArray)
CALL C_F_PTR(cPtrToData, arrayPtr, [n]) ! Associate the Fortran ptr
array(3:7) = ... ! Do whatever, no need to copy
Which is the rough equivalent to the C version:
mxArray* myMatlabArray = ...; //
mwSize n = mxGetNumberOfElements(myMatlabArray);
int* arrayPtr = (int*)mxGetData(myMatlabArray);
array[3] = ... // Do whatever, no need to copy
So in both cases these MEX functions could be called with Matlab array of Matlab type int32.
I have a Fortran main program under which there are many subroutines. One of the subroutine calls a c++ function. That c++ function is calling another Fortran subroutine. Now I need to compile all of them together to get the output.
I have tried to compile c++ file with icl. Then I have used ifort as a linker between Fortran file and object file created for the c++. But the method is not working. Its showing unresolved external symbol.
I expect that you at least saw this: https://software.intel.com/en-us/node/691954
What they wrote there mostly refers to C, though there is mention of C++ libraries. IF you link with C++ ocde, you need those.
You should read about symbol name mangling in C++. Because C++ supports overloaded funktions but linker requires unique symbols, C++ generates
something like _foo****#8 instead of foo for a function foo(int i, float j), where * depend on compiler and on type of arguments.
Fortran code generates C-styled symbols if BIND(C) used, you can force C++ to generate one for a function , by using extern "C" in C++ code both for prototypes of fortran functions called from C++ and for functions that will be called from fortran.
This example works with C compiler, but for C++ you need to change symbol generated (https://software.intel.com/en-us/node/691929#92BDCE7A-30FA-4A60-BCDB-7CE1521572EC). Note that C and Fortran interoperability isn't standardized and usually not portable from one compiler set to another. I had to deal with problem that Compaq compiler was mangling function names as well but not in C++ way, or that PGI Fortran required stdcall conventions.
Fortran Code Example
subroutine Simulation(alpha, beta, gamma, delta, arrays) BIND(C)
use, intrinsic :: ISO_C_BINDING
implicit none
integer (C_LONG), value :: alpha
real (C_DOUBLE), intent(inout) :: beta
integer (C_LONG), intent(out) :: gamma
real (C_DOUBLE),dimension(*),intent(in) :: delta
type, BIND(C) :: pass
integer (C_INT) :: lenc, lenf
type (C_PTR) :: c, f
end type pass
type (pass), intent(inout) :: arrays
real (C_FLOAT), ALLOCATABLE, target, save :: eta(:)
real (C_FLOAT), pointer :: c_array(:)
...
! Associate c_array with an array allocated in C
call C_F_POINTER (arrays%c, c_array, (/arrays%lenc/) )
...
! Allocate an array and make it available in C
arrays%lenf = 100
ALLOCATE (eta(arrays%lenf))
arrays%f = c_loc(eta)
...
end subroutine Simulation
C Struct declaration Example
struct pass {int lenc, lenf; float *c, *f;};
C Function Prototype Example
void simulation(long alpha, double *beta,
long *gamma, double delta[], struct pass *arrays);
C Calling sequence Example
simulation(alpha, &beta, &gamma, delta, &arrays);
I had read many posts here about mixing languages use of Fortran and C++. However, I'm still stuck with my current problem: my Fortran program always aborted.
I have the Fortran program: test-cc.f90 and the C++ program: deb_cc.cc.
deb_cc.cc contains:
#include <iostream>
using namespace std;
extern "C" void deb_cc_(float*** rh,int* x, int* y , int* z_ext )
{
cout <<"thinkdeb 1"<<endl;
int i, j, k;
cout <<"thinkdeb 1"<<endl;
cout <<"thinktest i=8,j=4,k=1"<< " (*x) " << (*x)<<endl;
cout <<"thinktest i=8,j=4,k=1"<< " x3/rh " << rh[1][1][1]<<endl; //abortion
// here
cout <<"thinkdeb 7"<<endl;
return;
}//end function
test-cc.f90 contains:
use ISO_C_BINDING
implicit none
interface
subroutine deb_cc( rh,x,y,z_ext)
use ISO_C_BINDING
implicit none
real(c_float),allocatable::rh(:,:,:)
integer(c_int):: x,y,z_ext
end subroutine
end interface
integer nx,ny,nz
parameter (nx=10,ny=10,nz=10)
real ,dimension (:,:,:),allocatable:: x1
integer:: iy1,iy2,iy3,iy4
integer i,j,k
allocate(x1(nx,ny,nz))
do k=1,nz
do j=1,ny
do i=1,nx
x1(i,j,k)=k*1000+j*100+i
enddo
enddo
enddo
iy1=nx
iy2=ny
iy3=nz
call deb_cc(x1,iy1,iy2,iy3)
end
I compiled them by pgf90 -c test-cc.f90 and pgcpp -c deb_cc.cc
Finally, I linked them by pgf90 -pgcpplibs test-cc.o deb_cc.o.
The output is:
thinktest in test- x1 (8,2,2) is 2208.000
thinkdeb 1
thinkdeb 1
thinktest i=8,j=4,k=1 (*x) 10
Segmentation fault (core dumped)
You use the iso_c_binding module, but your procedure interface is not C interoperable.
The iso_c_binding module is not the most important thing. The bind(C) attribute is the key. (I ranted several times about the unfortunate name of the tag here)
You use an assumed shape allocatable array argument
real(c_float),allocatable::rh(:,:,:)
these are not allowed in interoperable procedures in Fortran 2008, because C or C++ have no idea what to do with them. They are not just addresses. If you used the bind(C) attribute in the interface, the compiler should tell you it is wrong.
There is a possibility to pass them in the next Fortran standard (in an existing TS actually) using a special C header, but some compilers (notably gfortran) are still not compatible.
As you do not do any reallocation on the C side (at least in your example), you can just pass the array as an assumed size (array(*)) argument. I also changed the C++ name, no need for the underscore.
interface
subroutine deb_cc(rh,x,y,z_ext) bind(C, name="deb_cc")
use ISO_C_BINDING
real(c_float) :: rh(*)
integer(c_int):: x,y,z_ext
end subroutine
end interface
On the C side, you cannot use the C arrays which are pointers to pointers ([i][j][k]). What you receive from Fortran is a single block of memory. You also have to pass the array shape. At least in the first two Fortan dimensions.
I would just use a macro to index the array in C.
// adjust as needed, many variants possible
#define IND(i,j,k) = i + (j-1) * nx + (k-1) * nx * ny
// adjust as needed, many variants possible
extern "C" void deb_cc(float *rh, int *nx, int *ny, int *nz) {
cout <<"thinktest i=8,j=4,k=1"<< " x3/rh " << rh(IND(8,4,1))<<endl;
}
I'm working on a project that requires to store the memory address of an attribute of an derived type in Fortran. What a found is in gfortran, there is an implicit copy of the return of a function if the return is a derived type. So the address I stored is meaningless. There is a piece of code:
module atest
! use iso_c_binding
type test
real(8):: a
real(8):: p
end type test
interface
subroutine pointerprint(a)
real(8), intent(in) :: a
end subroutine pointerprint
end interface
interface assignment(=)
module procedure overloadedAsgn
end interface
contains
function returnvalue(t)
type(test), intent(in)::t
type(test):: returnvalue
returnvalue%a=t%a+1
call pointerprint(returnvalue%a)
end function returnvalue
SUBROUTINE overloadedAsgn(ret,rhs)
type(test), intent(inout) :: ret
type(test), intent(in) :: rhs
print *, "assign_d"
ret%a=rhs%a
ret%p=rhs%p+1.0d0
call pointerprint(rhs%a)
end SUBROUTINE overloadedAsgn
end module
program xxx
use atest
type(test):: t
type(test):: b
t%a=1.0d0/11.0d0
t%p=0.0d0
b=returnvalue(t)
end program
#include<stdio.h>
void pointerprint_(double *x){
printf("Addr<%lx>\n",(unsigned long)x);
}
The output using gfortran is:
Addr<7fff51df24e0>
assign_d
Addr<7fff51df2520>
The outpus using ifort is:
Addr<7fffc92e6fc0>
assign_d
Addr<7fffc92e6fc0>
Is there a way to avoid such a copy in gfortran so the result is consistent? I just begin to learn fortran. There might be a descriptor for the function or an option for the compiler.
You are counting on processor dependent behaviour. The processor may pass by copy-in (and/or out) if it is profitable. You may use pointer dummy arguments to pass a pointer.
You don't have any TARGET in your code and still want to rely on pointers to your variables.