C from Fortran Calls

D

deadpickle

I'm trying to mix my programming languages. I know Fortran very well
but C I'm not so knowledgable. When I call a C subroutine from Fortran
I use the scheme:

passer = TRIM(output_path)//CHAR(0)
PRINT *, "FORTRAN",passer,a
CALL create_shp(passer, a)

where:
INTEGER :: a
CHARACTER(200) :: passer

In C the subroutine is set as:
void create_shp_(char *output_path, int *a)
{
printf("C %s %d\n",output_path,a);
}

When I run the program I get an output:
FORTRAN /home/thor/output/ 35
C /home/thor/output/ -1074936500

The CHAR match but the int dont. Any Ideas?
 
I

Ian Collins

deadpickle said:
I'm trying to mix my programming languages. I know Fortran very well
but C I'm not so knowledgable. When I call a C subroutine from Fortran
I use the scheme:

passer = TRIM(output_path)//CHAR(0)
PRINT *, "FORTRAN",passer,a
CALL create_shp(passer, a)

where:
INTEGER :: a
CHARACTER(200) :: passer

In C the subroutine is set as:
void create_shp_(char *output_path, int *a)
{
printf("C %s %d\n",output_path,a);
}

When I run the program I get an output:
FORTRAN /home/thor/output/ 35
C /home/thor/output/ -1074936500

The CHAR match but the int dont. Any Ideas?

Without knowing the Fortran syntax, it looks like you are declaring 'a'
as an int on the Fortran side and as an int* (pointer to int) in the C
function. I'm guessing CHARACTER(200) maps to an array of char, which
will decay to a pointer to char which explains why that parameter works.
 
J

Jens Thoms Toerring

deadpickle said:
I'm trying to mix my programming languages. I know Fortran very well
but C I'm not so knowledgable. When I call a C subroutine from Fortran
I use the scheme:
passer = TRIM(output_path)//CHAR(0)
PRINT *, "FORTRAN",passer,a
CALL create_shp(passer, a)
where:
INTEGER :: a
CHARACTER(200) :: passer
In C the subroutine is set as:
void create_shp_(char *output_path, int *a)
{
printf("C %s %d\n",output_path,a);
}
When I run the program I get an output:
FORTRAN /home/thor/output/ 35
C /home/thor/output/ -1074936500
The CHAR match but the int dont. Any Ideas?

Your function gets passed a pointer to int (since Fortran does
call by reference) and thus the function definition looks good.
But then instead of printing the value of what 'a' points to you
print 'a' itself, i.e. the address. Changing the line
printf("C %s %d\n",output_path,a);

to

printf("C %s %d\n", output_path, *a);

probably will do the job, assuming that a C int has the same
size as a Fortran INTEGER. (Using 'output_path' the way you do
it is ok since printf() expects a pointer to a char anyway for
'%s').

If you actually want to print the address of 'a' and not the
value stored at 'a' then you should use

printf("C %s %p\n", output_path, (void *) a);

i.e. use '%p' instead of '%d' since that's what is to be used
for printing pointers, and cast the address to 'void *' since
this is what is expected by printf() for '%p'.

Regards, Jens
 
K

Keith Thompson

deadpickle said:
I'm trying to mix my programming languages. I know Fortran very well
but C I'm not so knowledgable. When I call a C subroutine from Fortran
I use the scheme:

passer = TRIM(output_path)//CHAR(0)
PRINT *, "FORTRAN",passer,a
CALL create_shp(passer, a)

where:
INTEGER :: a
CHARACTER(200) :: passer

In C the subroutine is set as:
void create_shp_(char *output_path, int *a)
{
printf("C %s %d\n",output_path,a);
}

When I run the program I get an output:
FORTRAN /home/thor/output/ 35
C /home/thor/output/ -1074936500

The CHAR match but the int dont. Any Ideas?

I don't know FORTRAN very well, but there's a problem in your C code.
In create_shp_(), ``a'' is a pointer to int. You print it with a
"%d" format, which requires an int, not a pointer. Garbage is to be
expected; actually the behavior is undefined. The value -1074936500
(0xbfedc54c with 2's-complement wraparound) seems plausible as a
32-bit address value -- or it might just be garbage.

Assuming that FORTRAN passes arguments by reference, changing
"a" to "*a" in the printf call is likely to fix the problem.
 
T

Tim Prince

I'm trying to mix my programming languages. I know Fortran very well
but C I'm not so knowledgable. When I call a C subroutine from Fortran
I use the scheme:

passer = TRIM(output_path)//CHAR(0)
PRINT *, "FORTRAN",passer,a
CALL create_shp(passer, a)

where:
INTEGER :: a
CHARACTER(200) :: passer

In C the subroutine is set as:
void create_shp_(char *output_path, int *a)
{
printf("C %s %d\n",output_path,a);
}

When you fail to use the Fortran iso_c_binding facility, you invoke
implementation dependent behavior. In particular, most Fortran
compilers will add a hidden string length argument somewhere in your
example, which the callee may or may not get away with ignoring.
Others have explained that only if you were lucky would you get away
with formatting a pointer with the %d specification. Several C
compilers will point out the error at compile time, if you ask.
These are good reasons for consulting your Fortran and C textbooks.
 
D

deadpickle

When you fail to use the Fortran iso_c_binding facility, you invoke
implementation dependent behavior.  In particular, most Fortran
compilers will add a hidden string length argument somewhere in your
example, which the callee may or may not get away with ignoring.
Others have explained that only if you were lucky would you get away
with formatting a pointer with the %d specification.  Several C
compilers will point out the error at compile time, if you ask.
These are good reasons for consulting your Fortran and C textbooks.

I don't know FORTRAN very well, but there's a problem in your C code.
In create_shp_(), ``a'' is a pointer to int. You print it with a
"%d" format, which requires an int, not a pointer. Garbage is to be
expected; actually the behavior is undefined. The value -1074936500
(0xbfedc54c with 2's-complement wraparound) seems plausible as a
32-bit address value -- or it might just be garbage.

Assuming that FORTRAN passes arguments by reference, changing
"a" to "*a" in the printf call is likely to fix the problem.

--
Keith Thompson (The_Other_Keith) (e-mail address removed) <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

Thanks for the replies. I have another question. Im trying to create
the appropriate file name to create a shapefile. I'm using this code
line:

sprintf(event,"%d",*a);
printf("%s\n",event);

strcat(tag,"track");
printf("%s\n",tag);

strcat(shpname,event);
printf("%s\n",shpname);

strcat(shpname,tag);
printf("%s\n",shpname);

strcat(lname,shpname);
printf("%s\n",shpname);

strcat(shpname,".shp");
printf("%s\n",shpname);

strcpy(pathshp,output_path);
strcat(pathshp,shpname);
printf("%s\n",shpname);

The output I get is this:
35
¬¿DŽ¬¿track
35
35¬¿DŽ¬¿track
¬¿track
¬¿track.shp
¬¿track.shp

Why is it so weird?
 
I

Ike Naar

I have another question. Im trying to create
the appropriate file name to create a shapefile. I'm using this code
line:

sprintf(event,"%d",*a);
printf("%s\n",event);

strcat(tag,"track");

What was the previous contents of ``tag''?
``tag'' must be a valid string if you use it as an argument for strcat.
printf("%s\n",tag);

strcat(shpname,event);

Ditto for shpname.
printf("%s\n",shpname);

strcat(shpname,tag);
printf("%s\n",shpname);

strcat(lname,shpname);

Ditto for lname.
printf("%s\n",shpname);

strcat(shpname,".shp");
printf("%s\n",shpname);

strcpy(pathshp,output_path);
strcat(pathshp,shpname);
printf("%s\n",shpname);

The output I get is this:
35
¬¿DŽ¬¿track
35
35¬¿DŽ¬¿track
¬¿track
¬¿track.shp
¬¿track.shp

Why is it so weird?

Most likely, uninitialized strings being passed to strcat.
Are you aware of the difference between strcpy and strcat?
 
I

Ian Bush

I'm trying to mix my programming languages. I know Fortran very well
but C I'm not so knowledgable. When I call a C subroutine from Fortran
I use the scheme:

passer = TRIM(output_path)//CHAR(0)
PRINT *, "FORTRAN",passer,a
CALL create_shp(passer, a)

where:
INTEGER :: a
CHARACTER(200) :: passer

In C the subroutine is set as:
void create_shp_(char *output_path, int *a)
{
        printf("C %s %d\n",output_path,a);

}

When I run the program I get an output:
FORTRAN /home/thor/output/ 35
C /home/thor/output/ -1074936500

The CHAR match but the int dont. Any Ideas?

Note the line wrap in the fortran code below, stupid google.

Wot now ? cat c.f90
Program pass_char

Use iso_c_binding, Only : c_char, c_int

Implicit None

Integer( Kind = c_int ) :: a = 10

Character( Len = 20 ) :: output_path
Character( Kind = c_char, Len = 20 ) :: passer

Interface
Subroutine create_shp( output_path, a ) Bind( c, Name =
'create_shp' )
Use iso_c_binding, Only : c_char, c_int
Implicit None
Character( Kind = c_char ), Dimension( * ), Intent( In ) ::
output_path
Integer ( Kind = c_int ) , Intent( In ) :: a
End Subroutine create_shp
End Interface

output_path = 'track.shp'

passer = Trim( output_path ) // Char( 0 )
Write( *, * ) 'Fortran ', passer, a
Call create_shp( passer, a )

End Program pass_char
Wot now ? cat cr_shp.c
#include <stdio.h>

void create_shp( char *output_path, int *a )
{
printf( "C %s %d\n", output_path, *a );
}
Wot now ? gfortran --version
GNU Fortran (Ubuntu 4.3.3-5ubuntu4) 4.3.3
Copyright (C) 2008 Free Software Foundation, Inc.

GNU Fortran comes with NO WARRANTY, to the extent permitted by law.
You may redistribute copies of GNU Fortran
under the terms of the GNU General Public License.
For more information about these matters, see the file named COPYING

Wot now ? gfortran -W -Wall -pedantic -std=f2003 -c c.f90
Wot now ? gcc -W -Wall -pedantic -std=c89 -c cr_shp.c
Wot now ? gfortran c.o cr_shp.o
Wot now ? ./a.out
Fortran track.shp 10
C track.shp 10
Wot now ? g95 -W -Wall -pedantic -std=f2003 -c c.f90
Wot now ? g95 c.o cr_shp.o
Wot now ? ./a.out
Fortran track.shp 10
C track.shp 10
Wot now ? f90 -V
f90: Sun Fortran 95 8.4 Linux_i386 2009/06/03
Usage: f90 [ options ] files. Use 'f90 -flags' for details
Wot now ? f90 -c c.f90
Wot now ? bin/cc -V
cc: Sun C 5.10 Linux_i386 2009/06/03
usage: cc [ options] files. Use 'cc -flags' for details
Wot now ? bin/cc -c cr_shp.c
Wot now ? f90 -c c.f90
Wot now ? gcc -W -Wall -pedantic -std=c89 -c cr_shp.c
Wot now ? ./a.out
Fortran track.shp 10
C track.shp 10

So you'll need a compiler that supports at least C interop in fortran
2003 (i.e. any modern fortran compiler) to do it this way. Also I have
half a feeling that mixing I/O in Fortran and C is not allowed, but I
can't find the reference at the moment, and anyway you might just want
it for this example. You might also want to look at the value
attribute. You'll certain want to look at your dead tree reference,
which is of course by your side at all times.

Follow ups probably best in clf, though I haven't set them. And the
spelling is Fortran, i.e. lower case, and has been for 2 decades,

Ian
 
P

Phred Phungus

deadpickle said:
Why is it so weird?

Because doing this is not easy, and I look this thread and see a mess.

If you trim it up and clean it up, you might be able to ask a better
question.
 
P

Phred Phungus

Keith Thompson wrote:

snip
I don't know FORTRAN very well, but there's a problem in your C code.
In create_shp_(), ``a'' is a pointer to int. You print it with a
"%d" format, which requires an int, not a pointer. Garbage is to be
expected; actually the behavior is undefined. The value -1074936500
(0xbfedc54c with 2's-complement wraparound) seems plausible as a
32-bit address value -- or it might just be garbage.

I recognize that number. How do you get to thinking of it as a
plausible address value?
 
K

Keith Thompson

Phred Phungus said:
Keith Thompson wrote:
snip

I recognize that number. How do you get to thinking of it as a
plausible address value?

Nothing more than a hunch. How do you recognize it?
 
D

deadpickle

As one of my persistent errors.  :(

Thanks for all the help. I'm trying to pass an array to my C code from
fortran. In C I have the variable declared:
double *shparray[2][*shpnum]
but when I go to print its contents I get:
for (a=0;a < *shpnum; a++)
{
//~ add point to geometry
printf("%lf,%lf\n",*shparray[1][a], *shparray[0][a]);
OGR_G_AddPoint(line, *shparray[1][a], *shparray[0][a], 0);
printf("Adding Point:\"%lf %lf\"\n",shparray[0][a],shparray[1][a]);
}

I get a segfault. Am I passing the array correctly?



void add_shp(int *shpnum,int *eventnum,double *shparray[2]
[*shpnum],double *shperror,char *passer,int *tracknum)
{
//~ variable declaration
OGRSFDriverH driver;
OGRDataSourceH ds;
const char *sprj = "+proj=lcc +lat_1=33.000000 +lat_2=45.000000
+lat_0=39.000000 +lon_0=-96.000000 +x_0=0.0 +y_0=0.0 +datum=NAD83";
const char *tprj = "WGS84";
OGRCoordinateTransformationH ctrans;
OGRSpatialReferenceH source, target;
OGRLayerH layer;
OGRGeometryH line;
OGRFeatureDefnH featureDefn;
OGRFeatureH feature;
char lname[10]="", shpname[20]="", event[5]="";
int a;

printf("%d,%f,%d,%s,%d\n",
*eventnum,*shperror,*shpnum,passer,*tracknum);

chdir(passer);

//~ create shp name
sprintf(event,"%d",*eventnum);
printf("%s\n",event);

strcpy(lname,event);
strcat(lname,"track");
printf("%s\n",lname);

strcat(shpname,event);
strcat(shpname,"track.shp");
printf("%s\n",shpname);

//~ OGRRegisterAll();
//~ driver = OGRGetDriverByName("ESRI Shapefile");

//~ Open shp
ds = OGROpen(shpname,TRUE,NULL);
if(ds == NULL) {
printf("Cant Open Shapefile!!\n");
return;
}

printf("Adding Feature to %s\n",shpname);

//~ create spatrefs and trans
source = OSRNewSpatialReference(NULL);
target = OSRNewSpatialReference(NULL);

OSRSetFromUserInput(source, sprj);
OSRSetFromUserInput(target, tprj);
ctrans = OCTNewCoordinateTransformation(target, source);

//~ Get layer
layer = OGR_DS_GetLayerByName(ds, lname);

//~ create geometry
line = OGR_G_CreateGeometry(wkbLineString);

//~ layer def and create feature
featureDefn = OGR_L_GetLayerDefn(layer);
feature = OGR_F_Create(featureDefn);

//~ loop through array and add points
for (a=0;a < *shpnum; a++)
{
//~ add point to geometry
printf("%lf,%lf\n",*shparray[1][a], *shparray[0][a]);
OGR_G_AddPoint(line, *shparray[1][a], *shparray[0][a], 0);
printf("Adding Point:\"%lf %lf\"\n",shparray[0][a],shparray[1][a]);
}

//~ transform points
OGR_G_Transform(line, ctrans);

//~ set geometry
OGR_F_SetGeometry(feature, line);

//~ create feature
OGR_L_CreateFeature(layer, feature);

//~ close ogr stuff
OGR_G_DestroyGeometry(line);
OGR_F_Destroy(feature);
OGR_DS_Destroy(ds);

return;
}
 
B

Ben Bacarisse

It's best not to quote sigs.
Thanks for all the help. I'm trying to pass an array to my C code from
fortran. In C I have the variable declared:
double *shparray[2][*shpnum]
but when I go to print its contents I get:
for (a=0;a < *shpnum; a++)
{
//~ add point to geometry
printf("%lf,%lf\n",*shparray[1][a], *shparray[0][a]);
OGR_G_AddPoint(line, *shparray[1][a], *shparray[0][a], 0);
printf("Adding Point:\"%lf %lf\"\n",shparray[0][a],shparray[1][a]);
}

I get a segfault. Am I passing the array correctly?

You code is correct for the given type but it is very unlikely that
Fortran will use that type in its C interface. (I've used a few and I
would not expect an array or pointers which is what you have here.)

Surely you will get better help in a Fortran group? This must be
bread and butter stuff for Fortran programmers.
void add_shp(int *shpnum,int *eventnum,double *shparray[2]
[*shpnum],double *shperror,char *passer,int *tracknum)

If I had to guess, I'd suggest double shparray[2][*shpnum] but that is
just a guess. Plain variables turn into pointers, but arrays in C get
passed as pointers by default so you probably don't need the extra *.
As I say, this is a guess.

Note also that this type is what C calls a variably modified type and
you'd have to check (did I suggest a Fortran group) that the Fortran
declaration you are using really does may to a variably modified array
in C (rather than to a fixed-sized array).

<snip code>
 
I

Ian Bush

Thanks for all the help. I'm trying to pass an array to my C code from
fortran. In C I have the variable declared:
double *shparray[2][*shpnum]
but when I go to print its contents I get:
for (a=0;a < *shpnum; a++)
        {
                //~ add point to geometry
                printf("%lf,%lf\n",*shparray[1][a], *shparray[0][a]);
                OGR_G_AddPoint(line, *shparray[1][a], *shparray[0][a], 0);
                printf("Adding Point:\"%lf %lf\"\n",shparray[0][a],shparray[1][a]);
        }

I get a segfault. Am I passing the array correctly?

void add_shp(int *shpnum,int *eventnum,double *shparray[2]
[*shpnum],double *shperror,char *passer,int *tracknum)

No idea. You've only posted half the problem. What does the Fortran
side look like - including
the declarations of all variables and the interface block; a complete
program showing the
problem is what would allow us to work out what is wrong quickest.

Better follow Ben's advice and post this with the info I've asked for
in clf. You'll get an answer
there.

(Actually a slight lie, shparray looks very suspicious - you are aware
of the difference between row
and column major? But I'm not pshycic, I can't guess quite how it is
wrong without both sides of the
problem)

Ian
 
M

Malcolm McLean

Thanks for all the help. I'm trying to pass an array to my C code from
fortran. In C I have the variable declared:
double *shparray[2][*shpnum]
but when I go to print its contents I get:
This looks like garbage. It probably means something in the particular
dialect of C that your compiler uses, but not what want it to mean.

When passing Fortran to C, it's best to simply collapse the variable
into a raw pointer. You have no type safety anyway, and C arrays are
in column-major as opposed to row-major order, and zero-based as
opposed to 1-based, so effectively an entire reinterpretation is
needed.
 
B

Ben Bacarisse

Malcolm McLean said:
Thanks for all the help. I'm trying to pass an array to my C code from
fortran. In C I have the variable declared:
double *shparray[2][*shpnum]
but when I go to print its contents I get:
This looks like garbage. It probably means something in the particular
dialect of C that your compiler uses, but not what want it to mean.

It may well be wrong, but it is valid C!

<snip>
 
P

Phred Phungus

Ben said:
Malcolm McLean said:
Thanks for all the help. I'm trying to pass an array to my C code from
fortran. In C I have the variable declared:
double *shparray[2][*shpnum]
but when I go to print its contents I get:
This looks like garbage. It probably means something in the particular
dialect of C that your compiler uses, but not what want it to mean.

It may well be wrong, but it is valid C!

<snip>

How about this, Ben?

$ gcc -D_GNU_SOURCE -std=c99 -Wall -Wextra g1.c -o out
g1.c: In function ‘fun’:
g1.c:7: warning: control reaches end of non-void function
$ ./out
5 0.000000
$ cat g1.c


#include <stdio.h>

int fun(int *n,float x[*n][*n]) {
printf("%d %f\n",*n,x[3][3]);
}


// test variable dimension dummy arrays
int main() {
int n=5;
float y[5][5];
fun(&n,y);

return 0;
}

// gcc -D_GNU_SOURCE -std=c99 -Wall -Wextra g1.c -o out
$

It has the same issue as OP's code, but I haven't figured out what that
is yet.
 
B

Ben Bacarisse

Phred Phungus said:
Ben said:
Malcolm McLean said:
Thanks for all the help. I'm trying to pass an array to my C code from
fortran. In C I have the variable declared:
double *shparray[2][*shpnum]
but when I go to print its contents I get:

This looks like garbage. It probably means something in the particular
dialect of C that your compiler uses, but not what want it to mean.

It may well be wrong, but it is valid C!
How about this, Ben?

Are you asking if the code below is valid? It prints an indeterminate
value which is at least pointless if not actually invalid.
$ gcc -D_GNU_SOURCE -std=c99 -Wall -Wextra g1.c -o out
g1.c: In function ‘fun’:
g1.c:7: warning: control reaches end of non-void function
$ ./out
5 0.000000
$ cat g1.c


#include <stdio.h>

int fun(int *n,float x[*n][*n]) {
printf("%d %f\n",*n,x[3][3]);
}


// test variable dimension dummy arrays
int main() {
int n=5;
float y[5][5];
fun(&n,y);

return 0;
}
It has the same issue as OP's code, but I haven't figured out what
that is yet.

It seems quite different to me. The OP's issue seemed to be about
calling C from Fortran.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,756
Messages
2,569,540
Members
45,024
Latest member
ARDU_PROgrammER

Latest Threads

Top