Let's assume a function that works for a string of any length str:
Two different alternatives came to my mind, but I am not sure they are exactly the same or which one is a better practice.
Way-1
function func_name(str) result(ouput)
character(len=*), intent(in) :: str
...
end function func_name
Way-2
function func_name(str) result(ouput)
character(len=:),allocatable, intent(in) :: str
...
end function func_name
The two approaches are not the same: one has a dummy argument with assumed length, and one with deferred length. These mean entirely different things.
That said, in this case there three main differences:
the actual argument for the second func_name must be allocatable/deferred length
the second func_name is such that it must have an explicit interface available when being referenced; the first need only have an implicit interface (which you'll be giving anyway, right?)
only in the second form can the dummy argument be passed on to an allocatable dummy
As to why there's little difference beyond that: the dummy argument is intent(in) (and not a pointer). This means that you can't change its value, you can't (in the second) change the allocation status. Simply, there's no reason for using deferred length if you can't change any property (except in the case of passing to other intent(in) allocatable/pointer dummies). Nearly any reference/use of the dummy argument will make can be done with either form.
Stick to using assumed-length when you can.
Finally, character length isn't a terribly special aspect to consider, so many of the points of a related question apply.
Why not try it in a code?
ijb#ijb-Latitude-5410:~/work/stack$ cat char.f90
Program test
Implicit None
Character( Len = 3 ) :: str1
Character( Len = : ), Allocatable :: str2
Allocate( Character( Len = 4 ) :: str2 )
Write( *, * ) func_name_1( str1 )
Write( *, * ) func_name_2( str1 )
Write( *, * ) func_name_1( str2 )
Write( *, * ) func_name_2( str2 )
Contains
Function func_name_1( str ) Result( output )
Implicit None
Integer :: output
Character( Len = * ), Intent( In ) :: str
output = Len( str )
End Function func_name_1
Function func_name_2 (str ) Result( output )
Implicit None
Integer :: output
Character( Len = : ), Allocatable, Intent( In ) :: str
output = Len( str )
End Function func_name_2
End Program test
ijb#ijb-Latitude-5410:~/work/stack$ gfortran-11 --version
GNU Fortran (GCC) 11.1.0
Copyright © 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
ijb#ijb-Latitude-5410:~/work/stack$ gfortran-11 -std=f2008 -Wall -Wextra -fcheck=all -g -O char.f90
char.f90:11:29:
11 | Write( *, * ) func_name_2( str1 )
| 1
Error: Actual argument at (1) to allocatable or pointer dummy argument ‘str’ must have a deferred length type parameter if and only if the dummy has one
So for the second function form to be correct the actual argument must have a deferred length, ( Len = : ). You generally want the first form.
Related
This question already has an answer here:
Fortran CHARACTER (LEN = *) only apply to dummy argument or PARAMETER
(1 answer)
Closed 1 year ago.
In the mainline, how to I declare "to_upper" and "string" so as to call this function?
Pure Function to_upper (str) Result (string)
! Changes a string to upper case
Implicit None
Character(*), Intent(In) :: str
Character(LEN(str)) :: string
Integer :: ic, i
program tescc
character (*) to_upper, to_lower
character (*) tes1,tes2
tes1='LoWs' tes2=to_upper(tes1)
print*,tes1,',',tes2
end
gfortran -o tescc tescc.f tescc.f:4:24: 4 |
character (*) tes1,tes2 |
1 Error: Entity with assumed character length at (1) must be a dummy argument or a PARAMETER tescc.f:4:29:
You would need to declare three strings, one for the function, one which is the input string, and another for the output string. You would first need to end this function. You need to say the function name is a string because that is what it returns.
This function does nothing so you would need to finish writing the function algorithm. But,
Here is an example code to run this function:
program main
implicit none
character(100) :: to_upper
character(100) :: some_str, other_str
some_str = "hello world"
other_str = to_upper(some_str)
end program main
pure function to_upper (str) Result (string)
! Changes a string to upper case
implicit None
Character(*), Intent(In) :: str
Character(LEN(str)) :: string
Integer :: ic, i
end function
I need to pass a string known at compile time from fortran to a c++ library. None of the pages in my search results have involved this particular distinction. Here is what the code looks like currently:
Fortran:
subroutine fci_wmm_associate_variable(array_name, array_pointer)
implicit none
character*100, intent(in), value :: array_name
type (c_ptr), intent(in) :: array_pointer
interface
subroutine wmm_associate_variable(name_f, pointer_f) bind (c)
use iso_c_binding
character (c_char), intent(in) :: name_f
type (c_ptr), intent(in), value :: pointer_f
end subroutine wmm_associate_variable
end interface
call wmm_associate_variable(array_name, array_pointer)
end subroutine fci_wmm_associate_variable
C++:
void wmm_associate_variable(char* varname, double* var_pointer)
{
//do stuf
}
This compiles fine, but my library needs the C++ function to look as follows:
void wmm_associate_variable(const char* varname, double* var_pointer)
{
//do stuf
}
I get an undefined reference when this happens:
undefined reference to `wmm_associate_variable'
How do I make it work for a const char*?
The Fortran-C interoperability feature works with C functions, consequently the C++ function needs to be declared with C linkage (extern "C").
(Note the Fortran declaration of the C function has the first argument as a default character kind scalar with length c_char - what you more than likely want is for it to be a c_char kind, length one assumed size array - character (KIND=c_char), intent(in) :: name_f(*) )
I am creating a C/C++ DLL which accepts char*[] (String array), changes the value in array and return back.
My C/C++ implementation:
int doubleijk_(char* data[]) // what should be the return type ???
{
// code to change array elements
return 0;
}
In Fortran (using ifort) I am calling the DLL's function like this:
module variables
type string
character(len=:), allocatable :: str
end type string
end module variables
program Call_CPP
use variables
type(string) :: array(3)
array = [string('xyza'), string('abcd'), string('mnopqrs')]
INTERFACE
! How to write SUBROUTINE for array
SUBROUTINE doubleijk_(arr) BIND(C,NAME='doubleijk_')
!???????WHAT SHOULD BE SUBROUTINE FOR ARRAY OF STRINGS????????
END SUBROUTINE doubleijk_
END INTERFACE
! Body of Call_CPP
call doubleijk_(array)
! print values of array after call
end program Call_CPP
I am able to pass string, integer from Fortran and got changed value from C/C++. What I need is to pass string array from Fortran to C/C++ and get back the array with changed values. How do I do this?
Finally made that working. Here is the solution.
program Call_CPP
use iso_c_binding, only: c_associated, c_loc, c_ptr
INTERFACE
SUBROUTINE doubleijk_(stringPtrs) BIND(C,NAME='doubleijk_')
use iso_c_binding
TYPE(C_PTR), DIMENSION(3) :: stringPtrs
END SUBROUTINE doubleijk_
END INTERFACE
TYPE(C_PTR), DIMENSION(3) :: stringPtr
CHARACTER(LEN=20), DIMENSION(3), TARGET :: stringArray
DO ns = 1, 3
stringArray(ns) = "My String"//char(0)
stringPtr(ns) = c_loc(stringArray(ns))
END DO
! Body of Call_CPP
call doubleijk_(stringPtr) !Call C function
DO ns = 1, 3
print *, stringArray(ns) !print array after call-changed values
END DO
end program Call_CPP
I'd like to learn the latest improvement in C++/FORTRAN interoperability when it comes to string in particular. The following is my unsuccessful attempt, please help me correct or advise a better solution.
My compiler is gcc 4.8.5
In C++
#include <iostream>
extern "C"{
void SayHello(char*);
}
int main(int argc, char** argv){
char * name = argv[1];
SayHello(name);
return 0;
}
In Fortran
module MyModule
contains
subroutine SayHello(people) bind(c,name="SayHello")
use, intrinsic :: iso_c_binding
character, dimension(50), intent(in) :: people
write(*,*) "Hello ", people
end subroutine
end module MyModule
Try using the c_char type:
character(kind=c_char), dimension(*), intent(in)
EDIT 1
So, after #francescalus posed the question, I looked into this further. Basically, the "assumed size" character array is not necessary1, although I do believe that the size of the char array is (please correct me if I'm wrong about that). I'm going to post a C-calling-Fortran version below, since I don't know the C++ syntax and don't feel like looking it up.
EDIT 2
As mentioned in footnote 1, it is only correct to declare people in the Fortran program as an assumed size array of chars, or (as suggested by #VladimirF) with the size given directly by sz. I clear this up in the code below.
The Fortran program:
! SayHello.f90
subroutine SayHello(people,sz) bind(c,name="SayHello")
use, intrinsic :: iso_c_binding
implicit none
! Notes:
! The size `sz` of the character array is passed in by value.
! Declare `people` as an assumed-size array for correctness, or just use the size `sz` passed in from C.
character(kind=c_char), intent(in), dimension(sz) :: people
integer(kind=c_int), intent(in), value :: sz
write(*,*) "Hello, ", people(1:sz)
end subroutine
And the C program:
/*Hello.c */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void SayHello(char *name, int len);
int main(int argc, char** argv){
size_t sz = strlen(argv[1]);
char * name = malloc(sz+1);
strcpy(name, argv[1]);
SayHello(name, sz+1);
free(name);
return 0;
}
Compilation (with ifort), call, and output:
ifort /c SayHello.f90
icl Hello.c /link SayHello.obj
Hello.exe MattP
// output: Hello, MattP
1Update: Seems like the official usage "for interoperability" is to declare as an array of characters, using assumed size: char(len=1,kind=c_char), dimension(*), intent(in)
I just want to read a two-column text file with variable lines into an array.
The first column of the text file is time in the unit of second, and the second is temperature. Just like this:
1.1 10
2.1 20
3.2 30
4.2 40
5.3 50
6.3 60
7.4 70
Following is the code I write:
module myData
type line_info
real :: time
real :: temp
end type
type datalink
type(line_info) :: time_temp
type(datalink), pointer :: next
end type
type(line_info), allocatable :: FileInfoArr(:)
end module
program Console1
use myData
implicit none
! Variables
type(line_info),allocatable :: time_temp_arr(:)
!real,allocatable :: File2Arr(:)
character (len=80) :: FileFullName="C:\t.txt"
call File2Arr(FileFullName,time_temp_arr)
End Program Console1
Subroutine File2Arr(FileFullName,InfoArray)
use myData
character (len=80) :: FileFullName
type(line_info),allocatable :: InfoArray(:)
type(datalink), pointer :: head
type(datalink), pointer :: p
integer error,size,i
logical alive
! check if file exists
inquire(file=FileFullName, exist=alive)
if(alive==0) then
write(*,*) FileFullName, "doesn't exist."
stop
end if
! read file using pointer
open(10, file=FileFullName, status="old", iostat=error)
if(error/=0) then
write(*,*) "open file fail!"
stop
end if
allocate(head)
nullify(head%next)
p=>head
size=0
!read(10,"(A80)") tempstr
do while(.true.)
read(10, fmt=*, iostat=error) p%time_temp
if(error/=0) exit
size=size+1
allocate(p%next, stat=error) ! add next line
if(error/=0) then
write(*,*) "Out of memory!"
stop
end if
p=>p%next
nullify(p%next)
end do
!save link info into an array
allocate(InfoArray(size))
p=>head
i=0
do while(associated(p%next))
i=i+1
InfoArray(i)=p%time_temp
p=>p%next
!write(*,*) FileInfoArr(i)%time, FileInfoArr(i)%temp
end do
End Subroutine
When I compile it, I got this :
error #8055: The procedure has a dummy argument that has the ALLOCATABLE, ASYNCHRONOUS, OPTIONAL, POINTER, TARGET, VALUE or VOLATILE attribute. Required explicit interface is missing from original source. [TIME_TEMP_ARR]
Any idea on how to fix this error, thanks for any help.
Put your subroutine File2Arr inside the module MyData (and remove the use mydata line inside that subroutine). It compiled & ran for me doing that.
Alternatively to adding your subroutine File2Arr to your module MyData, you could put it also directly in your program by using the CONTAINS keyword (here you can also remove the use mydata line in the subroutine).