Passing string from Fortran to c++ [duplicate] - c++

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.

Related

Entity with assumed character length at (1) must be a dummy argument or a PARAMETER [duplicate]

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

How do I pass a fortran string to const char* in c++ using fortran-C binding?

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(*) )

Passing string array from Fortran to C/C++ DLL and get array with changed values

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

What is the official way to deal with string in C++/FORTRAN interop

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)

FORTRAN function returning an array causes a segfault (calling from C++)

Basically, here's my problem. I'm calling someone else's FORTRAN functions from my C++ code, and it's giving me headaches. Some code:
function c_error_message()
character(len = 255) :: c_error_message
errmsg(1:9) = 'ERROR MSG'
return
end
That's the FORTRAN function. My first question is: Is there anything in there that would cause a segfault?
If not, then second: What does that return? A pointer?
I'm trying to call it with the following C statement:
char *e = c_error_message_();
That causes a segfault.
c_error_message();
That too causes a segfault.
I declared c_error_message_() earlier on with the following code:
extern"C" {
char* c_error_message_();
}
Would declaring a function with a different return type than the actual return type cause a segfault?
I'm at a loss. Thanks for any replies.
Here is a method that works. When I tried to use the ISO C Binding with a function returning a string, the compiler objected. So if instead you use a subroutine argument:
subroutine fort_err_message (c_error_message) bind (C, name="fort_err_message")
use iso_c_binding, only: C_CHAR, C_NULL_CHAR
character (len=1, kind=C_CHAR), dimension (255), intent (out) :: c_error_message
character (len=255, kind=C_CHAR) :: string
integer :: i
string = 'ERROR MSG' // C_NULL_CHAR
do i=1, 255
c_error_message (i) = string (i:i)
end do
return
end subroutine fort_err_message
The Fortran is a bit awkward because technically a C-string is an 1D array of characters.
And example C code to demo that this works:
#include <stdio.h>
void fort_err_message ( char chars [255] );
int main ( void ) {
char chars [255];
fort_err_message ( chars );
printf ( "%s\n", chars );
return 0;
}
Since the second line declares a name that is the same as the function name, it is declaring the type of the function return, namely a scaler character string of 255 characters. But in theory this doesn't tell us the internal API -- that is up the the compiler. I don't know where "errmsg" comes from -- it must be declared elsewhere -- perhaps a global variable as suggested by Michael Anderson. Or maybe this is a mistake and the line should be c_error_message = "ERROR MSG". (There is no need to designate the sub-string range -- the rest of the string will be filled with blanks.) IMO, the best approach to calling Fortran from C (or vice-a-versa) is to use the ISO C Binding, which will cause the Fortran compiler to use a C compatible Interface. I haven't done a function returning a string, but have done strings as arguments.
FORTRAN functions return the value assigned to the function name. The type returned is specified by the function definition, and in this case, it is returning an character string 255 characters long. I think the type mismatch is why you are segfaulting. So what C type should you use? I don't know.
I found this Using C with FORTRAN page, and the author strongly recommends using FORTRAN subtroutines and calling them as C functions returning type void.
Here is an F95 quick reference.