- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi,
I am trying to learn how to call Fortran from C, I am able to create .so file from C and call it from Ruby On Rails,
the C code is:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
long add(long maxn, double delta, double conf, char *errMsg)
{
long ERR_MSG_LENGTH, answer;
unsigned i;
char * iarr;
iarr = "Hello!";
answer = (long) (maxn+delta+conf);
for (i=0; ((char) iarr) != '\0'; ++i) errMsg = (char) iarr;
errMsg = '\0';
return answer;
}
but when I tried to call Fortran add function from C, I have trouble:
add.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
extern addsize(long maxn, double delta, double conf, long * sum);
long add(long maxn, double delta, double conf, char *errMsg)
{
long ERR_MSG_LENGTH, answer;
unsigned i;
char * iarr;
iarr = "Hello!";
addsize(maxn, delta, conf, &answer);
for (i=0; ((char) iarr) != '\0'; ++i) errMsg = (char) iarr;
errMsg = '\0';
return answer;
}
addsize.f90:
subroutine addsize( maxn, delta, conf, sum ) bind(c, name='addsize')
use, intrinsic :: iso_c_binding
!use program_constants
implicit none
real(C_LONG), intent(in) :: maxn
real (C_DOUBLE), intent(in) :: delta, conf
real(C_LONG), intent(out) :: sum
sum = maxn + delta + conf;
end subroutine addsize
I compile addsize.f90 as below:
/opt/intel/bin/ifort -c addsize.f90
then compile add.c
gcc -c -shared -fPIC add.c
then create add.so
gcc -shared -fPIC *.o -o add.so
Somehow I couldn't get it to work, is anything wrong in the code? Thanks!
Liz
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
lxh37 wrote:
Well, when I comment out call to Fortran, c code works fine and c code is so simple...
Must something wrong in the interface between c and Fortran.
Liz
@Liz,
As Steve said, it would appear the code you have posted should work, however it is spread across several messages and it's unclear if there are any differences between what you show here and in your actual system which could be causing the problem.
So why don't we try something different? Listed below is a somewhat simplified version of code for what you are trying to do that tries to use standard Fortran and C. It works on my system and gives the output shown below. Can you take this as-is and build execute on your system without any code changes and see what output you get? Post here if your output is different. This might help you figure out what is going on.
module m use, intrinsic :: iso_c_binding, only : c_char, c_null_char, c_long, c_double implicit none private public :: addsize contains function addsize( maxn, delta, conf, msgLen, Msg ) result( answer ) bind(c, name='addsize') !.. argument list integer(c_long), value, intent(in) :: maxn real(c_double), value, intent(in) :: delta real(c_double), value, intent(in) :: conf integer(c_long), value, intent(in) :: msgLen character(kind=c_char,len=1), intent(inout) :: Msg(msgLen) !.. function result integer(c_long) :: answer !.. local variables integer :: i integer(c_long) :: LenMsg character(kind=c_char,len=:), allocatable :: errmsg !.. Message errmsg = "hello" LenMsg = min( msgLen-1, len_trim(errmsg) ) forall (i=1:LenMsg) Msg(i) = errmsg(i:i) end forall !.. Append C null char Msg(LenMsg+1) = c_null_char answer = int( maxn + delta + conf ) ! the sample size return end function addsize end module m
#include <stdio.h> #include <string.h> // function prototypes extern long addsize(long maxn, double delta, double conf, long msgLen, char *Msg); #define ERR_MSG_LENGTH 70 int main() { long result, maxn; double delta, conf; char errMsg[ERR_MSG_LENGTH]; maxn = 1000; delta = 3.2; conf = 6.8; strncpy(errMsg, "There!", (size_t)ERR_MSG_LENGTH); printf("errMsg before calling Fortran function addsize: %s\n", errMsg); result = addsize(maxn, delta, conf, (long)ERR_MSG_LENGTH, errMsg); printf("result=%ld\n", result); printf("errMsg after function call: %s\n", errMsg); return 0; }
errMsg before calling Fortran function addsize: There! result=1010 errMsg after function call: hello Press any key to continue . . .
By the way, you have mixed-mode arithmetic in your Fortran code computing answer that adds an integer with two floating point variables and then casts back to an integer - is this really what you intended? This can cause unexpected behavior unless one is very careful.
P.S.> Are you at Penn State? Go Nittany Lions!
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
You need to add the VALUE attribute to the Fortran declaration of maxn, delta and conf. Like this:
real(C_LONG), value, intent(in) :: maxn
real (C_DOUBLE), value, intent(in) :: delta, conf
Your C prototype has these passed by value, so you need to say that in Fortran.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thanks for the suggestions, I add the VALUE attribute to the Fortran as you suggested, but still have trouble.
Liying
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
The declarations of maxn and sum in the Fortran code needs to be "integer(C_LONG)", not "real(C_LONG)".
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Great, it works now! I guess that probably Mac version is more forgiven when I used DLLEXPORT, and didn't use iso_c_binding. I will try to rewrite fortran code using iso_c_binding. Only the function called from C needs to use iso_c_binding, the rest functions don't, right?
Thanks very much for your help!
Liz
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I have anotehr question, if my fortran function is:
integer(our_int) function samplesize( maxn, delta, conf, msgLen, iMsg ) result( answer )
how to use iso_c_binding?
Can I do:
integer(our_int) function samplesize( maxn, delta, conf, msgLen, iMsg ) bind(c, name='samplesize')
or I have to use subroutine without returning a value? (I only find document for subroutine when call Fortran
from C).
Thanks!
Liz
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I would suggest:
function samplesize( maxn, delta, conf, msgLen, iMsg ) result(answer) bind(c, name='samplesize')
integer(our_int) :: answer
I assume you have made "our_int" available in code not shown.
ISO_C_BINDING provides declarations of various "kinds" that correspond to C types, plus some useful functions and types. You don't HAVE to use it, but it's more portable if you do. Also, you don't need ", name='samplesize'" in the BIND clause because BIND(C) automatically downcases the name.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thanks! I will rewrite the simple Fortran code I had using
function add( maxn, delta, conf, msgLen, iMsg ) result(answer) bind(c, name='samplesize')
integer(our_int) :: answer
to see if I can make it work.
If I don't use ISO_C_BINDING, I don't need this line:
use, intrinsic :: iso_c_binding
but I still need to use "value" if I pass by value? I will see if i Can get it working.
Thanks!
Liz
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
The combination of BIND(C) and VALUE is what you need. ISO_C_BINDING isn't relevant to pass-by-value.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi,
I can get it working without int * iMsg . I must did something wrong when I
add it. Please take a look? out_int, our_double are all defined in program_constants,
they are equivalent to C_LONG, C_DOUBLE.
In add.c, I declare
extern long addsize(long maxn, double delta, double conf, long msgLen, int *iMsg);
Fortran code below:
integer(our_int) function addsize( maxn, delta, conf, msgLen, iMsg ) result( answer ) bind(c, name='addsize')
!
use program_constants
implicit none
integer(our_int), value, intent(in) :: maxn
real(our_dble), value, intent(in) :: delta, conf
integer(our_int), value, intent(in) :: msgLen
integer(kind=our_int), intent(inout) :: iMsg(msgLen)
integer(our_int) :: i
character(len=70) :: errmsg
errmsg = "hello"
do i=1,msgLen
iMsg(i) = iachar( errmsg(i:i) )
end do
! insert a "null" at end of error string (for C)
iMsg( len_trim( errmsg ) + 1 ) = 0
answer = int( maxn + delta + conf ); ! the sample size
return
end function addsize
Thanks a lot!
Liz
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
It's hard for me to check for errors with only parts of the program, and also when you don't say what goes wrong.
I see that you've now added a character argument, but I don't see what iMsg is on the C side. You pass a pointer to... something... and treat it as a character string on the Fortran side.
At this point I suggest that you step through the program with a debugger and trace the paths and values to whatever it is that goes wrong. This should give you a clue as to how to fix it.
You had initially said that the code worked on Mac, but later said you had made changes. The Mac code should have worked on Linux with nothing more than dropping DLLEXPORT.
If you get stuck, please come back with a small but complete program, with the all of the C and Fortran code included, and we may be able to help further.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Now I puzzled how the codes (they are legacy codes and don't use bind and VALUE) could work in Mac,
the first thing I did earlier in the week is commenting out DLLEXPORT and compile in Linux, but the code will not work. So I tried to read documents how to call Fortran from C and implemented a very simple example which only have VALUE inputs, thank you for your help, and I can call it successfully from Ruby On Rails, When you told me that I don't have to use iso_c_binding, I tried today to make the simple example similar as the legacy code ( tried to see if I don't have to revise too much to make the code work). As you spot the issues so quickly yesterday, I thought if I just post Fortran code, you could tell if there is any obvious error. The legacy code tried to avoid passing string from Fortran to C, it uses int * instead and cast it to C later after getting it from Frotran. Let me send you too the simple C code: add.c which will call Fortran code addsize.f90 I sent this morning, I couldn't see anything wrong....
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
extern long addsize(long maxn, double delta, double conf, long msgLen, int *iMsg);
long add(long maxn, double delta, double conf, char *errMsg)
{
long ERR_MSG_LENGTH, answer;
unsigned i;
int *iarr;
ERR_MSG_LENGTH = 70;
iarr = (int *) malloc(ERR_MSG_LENGTH*sizeof(int));
for (i=0; i < ERR_MSG_LENGTH; i++) iarr = (int) ' ';
answer = addsize(maxn, delta, conf, ERR_MSG_LENGTH, iarr);
for (i=0; ((char) iarr) != '\0'; ++i) errMsg = (char) iarr;
errMsg = '\0';
return answer;
}
add.h
//
// add.h
// add
//
// Created by lxh37 on 11/19/2015.
// Copyright (c) 2015 Penn State. All rights reserved.
//
#ifndef add_h
#define add_h
long add(long maxn, double delta, double conf, char *errMsg);
and constants.f90 which defines our_int and our_dble (could use C_DOUBLE instead)
!#######################################################################
module program_constants
USE, INTRINSIC:: ISO_C_BINDING
implicit none
public ! unlike other modules, everything here is public
integer, parameter :: our_dble=selected_real_kind(15,307), &
our_int=C_LONG
end module program_constants
!#######################################################################
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I mean "avoid passing string from Fortran to C, it uses int * instead and cast it to char array later after getting int * back from Frotran".
The issue is mainly with additional int *, when I delete it, revise and comment out all codes related to it, it works.
Thanks!
Liz
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
What doesn't work? What goes wrong? It would be easier if you could add a short C program that calls "add".
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I generated .so and called from Ruby On Rails, only error message I got
is error with external library, guess there must be issue with my code.
I created this simple call_add.c below to call add,
#include<stdio.h>
int main(int argc, char **argv) {
long result, maxn;
double delta, conf;
char *errMsg;
maxn = 1000;
delta = 3.2;
conf = 6.8;
errMsg = "There!";
result = add(maxn, delta, conf, errMsg);
printf("%d\n",result);
return;
}
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thanks. Your Fortran-C code works perfectly. There is a problem with this C line after the Fortran call returns,
for (i = 0; ((char)iarr) != '\0'; ++i) errMsg = (char)iarr;
I'm not a C expert but I think the issue is that errMsg is pointing to a constant which you're not allowed to overwrite.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
A literal string in C code is placed in the data segment. Whether that is a writable segment or a read-only segment depends on the C compiler and the options given to the C compiler. Perhaps the real issue is when one passes a string pointer to a subprogram when that subprogram writes beyond the end of the literal string to which the pointer was set, without knowing or caring about the allocated length of the target string. Here is C code to illustrate this point.
#include <stdio.h> void sub(char *s){ char *p="This is a long string"; while(*p)*s++=*p++; *p='\0'; // Intentional bug here! Try also *s='\0' instead of *p='\0' } main(){ char *s="Initial String"; char *p="Sacrificial string"; sub(s); puts(s); puts(p); }
When I compile (on Windows) this with cl /Od /MD, the output is
This is a long stringficial string tringficial string
which is evidence of the clobbering that occurred without triggering any access violations. If I compile (on Windows) with cl /O2 /MD, the program crashes.
In the example code of #15, errmsg points to a character array that is 6+1 bytes long. However, this pointer is passed to another function in which a much longer string is written into the memory that it points to.
Such errors in C can be difficult to catch, as a result of which they can go undetected for long, but tools such as Intel Inspector can help.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Steve and mecej4. thanks for your help! I think the bug is still in Fortran. I commented out
for (i=0; ((char) iarr) != '\0'; ++i) errMsg = (char) iarr;
errMsg = '\0';
and add:
char *iarr1;
iarr1 = "Hello!";
for (i=0; ((char) iarr1) != '\0'; ++i) errMsg = (char) iarr1;
errMsg = '\0';
in add.c, but the program crashes,
If I commented out call to Fortran function
//answer = addsize(maxn, delta, conf, ERR_MSG_LENGTH, iarr);
answer = 100;
char *iarr1;
iarr1 = "Hello!";
for (i=0; ((char) iarr1) != '\0'; ++i) errMsg = (char) iarr1;
errMsg = '\0';
The program works fine.
I will try to increase the length of errMsg, see if that helps.
helps...
Liz
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
As best as I can tell, the Fortran code is working correctly.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Fortran code seems good to me too, I have passed
ERR_MSG_LENGTH = 70 to Fortran and only assigned a short string
"Hello!", so it should be fine. I feel the crash should be in C where errMsg
passed in from call_c.add is short...
for (i=0; ((char) iarr1) != '\0'; ++i) errMsg = (char) iarr1;
errMsg = '\0';
Don't know why it stlll crashes even I comment out the above...
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I suggest you take your C driver code and spend some time in gdb to understand what is going wrong.
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page