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).
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'm trying to get the filename of an HDF5 object in Fortran, for which I do not know the length of the filename in advance. The HDF5 documentation says this can be accomplished by passing NULL to h5fget_name:
If the length of the name, which determines the required value of size, is unknown, a preliminary H5Fget_name call can be made by setting name to NULL.
However, it is unclear how to accomplish this from Fortran code. Per the above language in the documentation, something like this should work:
function get_hdf5_filename(obj_id) result(filename)
use hdf5
use iso_c_binding, ONLY: c_null_ptr
! Arguments
integer(HID_T), intent(in)::obj_id
!! Handle of HDF5 object
! Result
character(:), allocatable::filename
integer(SIZE_T)::size = 0
!! Length of filename
integer::hdferr
!! HDF5 error code
call h5fget_name_f(obj_id, c_null_ptr, size, hdferr)
allocate (character(size)::filename)
call h5fget_name_f(obj_id, filename, size, hdferr)
end function get_hdf5_filename
However, gfortran won't compile the above and gives a type mismatch error:
Error: Type mismatch in argument 'buf' at (1); passed TYPE(c_ptr) to CHARACTER(1)
A null can be passed by declaring the filename variable as a pointer and nullifying it before the initial call to h5fget_name_f:
function get_hdf5_filename(obj_id) result(filename)
use hdf5
use iso_c_binding, ONLY: c_null_ptr
! Arguments
integer(HID_T), intent(in)::obj_id
!! Handle of HDF5 object
! Result
character(:), pointer::filename
integer(SIZE_T)::size = 50
!! Length of filename
integer::hdferr
!! HDF5 error code
nullify(filename)
call h5fget_name_f(obj_id, filename, size, hdferr)
! HDF5 needs one more character (probably for the null character terminating the string), so we allocate filename to length size+1
allocate (character(size+1)::filename)
call h5fget_name_f(obj_id, filename, size, hdferr)
! Remove last character from the returned string
filename => filename(1:size)
end function get_hdf5_filename
Note that h5fget_name_f needs an extra character in the passed string (probably for the terminating null character), so the name pointer has to be allocated one character longer than the value set in the size argument. The terminating null can be removed by redirecting the file pointer to a subset that excludes the last character using array slicing syntax (filename => filename(1:size) in the above example).
A possible solution is to create a C function that returns the filename length for a given HDF5 object:
#include "hdf5.h"
#include "H5f90i.h"
int_f get_hdf5_filename_length(hid_t_f *obj_id){
return H5Fget_name((hid_t)*obj_id, NULL, 0);
}
which can be called from Fortran in the following way:
function get_hdf5_filename(obj_id) result(filename)
use hdf5
use iso_c_binding, ONLY: c_null_ptr
interface
function get_hdf5_filename_length(obj_id) bind(c) result(length)
use hdf5
integer(HID_T)::obj_id
integer(SIZE_T)::length
end function get_hdf5_filename_length
end interface
! Arguments
integer(HID_T), intent(in)::obj_id
!! Handle of HDF5 object
! Result
character(:), pointer::filename
integer(SIZE_T)::size = 50
!! Length of filename
integer::hdferr
!! HDF5 error code
integer::i
!! Loop counter
size = get_hdf5_filename_length(obj_id)
! filename has to be allocated to size+1 to allow for the terminating null
! of the filename string in C
allocate (character(size+1)::filename)
! h5fget_name_f uses len_trim to determine the buffer length,
! which requires the buffer be filled with non-whitespace characters
! in order to work correctly
do i = 1, size + 1
filename(i:i) = 'a'
end do
! Get the filename
call h5fget_name_f(obj_id, filename, size, hdferr)
! Remove the null character from the end of the string
filename => filename(1:size)
end function get_hdf5_filename
Note that it is not enough to allocate the string buffer to the correct size; it also needs to be populated with non-whitespace characters before being passed to h5fget_name_f, because h5fget_name_f calls len_trim on the passed buffer and uses the result to determine the maximum permissible filename length.
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
This question already has answers here:
passing char arrays from c++ to fortran
(3 answers)
Closed 5 years ago.
I am following the following example (http://fortranwiki.org/fortran/show/Fortran+and+Cpp+objects) to adapt a C++ code to Fortran, first I am trying to learn Fortran and making connection between Fortran and C++.
I modified the code where I would like to pass a string from Fortran to C++
#include <iostream>
#include <string>
using namespace std;
class CWLiDAR {
string name;
public:
CWLiDAR(string);
};
CWLiDAR::CWLiDAR(string name)
{
this->name = name;
}
/*
*/
/* C wrapper interfaces to C++ routines */
#ifdef __cplusplus
extern "C" {
#endif
CWLiDAR* CWLiDAR__new(string name)
{
return new CWLiDAR(name);
}
void CWLiDAR__delete(CWLiDAR* This)
{
delete This;
}
#ifdef __cplusplus
}
#endif
The Fortran wrapper
module CWLiDAR_module
use, intrinsic :: ISO_C_Binding!, only: C_int, C_ptr, C_NULL_ptr
implicit none
private
type CWLiDAR_type
private
type(C_ptr) :: object = C_NULL_ptr
end type CWLiDAR_type
interface
function C_CWLiDAR__new (name, name_len) result(this) bind(C, name = "CWLiDAR__new")
import
type(C_ptr) :: this
character(name_len, kind=C_CHAR), intent(IN) :: name
integer :: name_len
end function C_CWLiDAR__new
subroutine C_CWLiDAR__delete (this) bind(C, name = "CWLiDAR__delete")
import
type(C_ptr), value :: this
end subroutine C_CWLiDAR__delete
end interface
interface new
module procedure CWLiDAR__new
end interface new
interface delete
module procedure CWLiDAR__delete
end interface delete
public :: new, delete, CWLiDAR_type
contains
! Fortran wrapper routines to interface C wrappers
subroutine CWLiDAR__new(this, name, name_len)
type(CWLiDAR_type), intent(out) :: this
character(*) :: name
integer :: name_len
this%object = C_CWLiDAR__new(name, name_len)
end subroutine CWLiDAR__new
subroutine CWLiDAR__delete(this)
type(CWLiDAR_type), intent(inout) :: this
call C_CWLiDAR__delete(this%object)
this%object = C_NULL_ptr
end subroutine CWLiDAR__delete
end module CWLiDAR_module
The main
program main
use cwlidar_module
type(CWLiDAR_type) :: lidar
call new(lidar, "Test", 4)
call delete(lidar)
end program main
How should I modify the Fortran wrapper to pass a string from Fortran to C++?
First of all, you cannot use std::string at the inteface between Fortran and C. std::string is some C++ implementation defined object that has no guaranteed memory layout for interoperability with Fortran.
You must use plain C strings instead. So, CVLiDAR__new() must accept an argument of type char*, and the fortran interface must declare this argument as
character(kind = c_char) :: name(*)
The Fortran wrapper routine must take some Fortran character dummy argument, and proceed to copy its contents into a suitably allocated character(kind = c_char) array, which is then passed to the C function.
At this point, your C interface function may proceed to convert it again into a C++ std::string. This conversion is optional, as C++ can handle C strings just as well as C can. Adding the conversion would allow the rest of the code be more pure C++, though.
Do not forget to correctly terminate the C string with a zero character when you assemble it on the Fortran side!
These argument conversions are indeed a PITA, and if you have any number of interface functions that you need to call from Fortran, it may be advisable to write a generator script that produces the Fortran wrappers. An example of such a script can be found within the Climate Data Interface library (CDI, source code can be found at https://code.mpimet.mpg.de/projects/cdi/files) in the file cdi-1.8.1/interfaces/f2003/bindGen.rb, its output is published in the same tar-ball under cdi-1.8.1/src/mo_cdi.f90. This script may be complete overkill for your case, but it works quite fine for CDI and its output may inspire/help you getting ideas of how to do your conversions right.
You CANNOT pass a C++ string (actually std::string) from Fortran. It is simply not compatible with C or Fortran, it is an internal C++ type. You have to use the C wrapper and pass C compatible character arrays.